jskelet 0.2.5 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/AGENTS.md +132 -132
  2. package/CHANGELOG.md +403 -383
  3. package/LICENSE +21 -21
  4. package/bin/jskelet.mjs +103 -103
  5. package/docs/01-baslangic.md +285 -285
  6. package/docs/02-mimari.md +293 -287
  7. package/docs/03-routing.md +486 -480
  8. package/docs/04-render-ve-sablonlar.md +490 -490
  9. package/docs/05-islands.md +482 -482
  10. package/docs/06-cache.md +1231 -1209
  11. package/docs/07-yapilandirma.md +44 -21
  12. package/docs/08-build.md +366 -366
  13. package/docs/09-dev-araclari.md +335 -335
  14. package/docs/10-dagitim.md +329 -329
  15. package/docs/11-tasima.md +1 -0
  16. package/docs/12-panel-ve-oturum.md +384 -384
  17. package/docs/README.md +105 -105
  18. package/docs/en/01-getting-started.md +292 -292
  19. package/docs/en/02-architecture.md +311 -305
  20. package/docs/en/03-routing.md +503 -497
  21. package/docs/en/04-rendering.md +504 -504
  22. package/docs/en/05-islands.md +492 -492
  23. package/docs/en/06-caching.md +1197 -1198
  24. package/docs/en/07-configuration.md +1009 -986
  25. package/docs/en/08-build.md +383 -383
  26. package/docs/en/09-dev-tools.md +342 -342
  27. package/docs/en/10-deployment.md +332 -332
  28. package/docs/en/11-migration.md +360 -359
  29. package/docs/en/12-dashboards-and-sessions.md +392 -392
  30. package/docs/en/README.md +112 -112
  31. package/package.json +102 -102
  32. package/src/build/ensure-build.mjs +15 -15
  33. package/src/build/paths.mjs +143 -143
  34. package/src/build/resolve-peer.mjs +36 -36
  35. package/src/build/tasks/client.mjs +268 -268
  36. package/src/build/tasks/css.mjs +124 -124
  37. package/src/build/tasks/fonts.mjs +146 -146
  38. package/src/build/tasks/icons.mjs +224 -224
  39. package/src/build/tasks/images.mjs +244 -244
  40. package/src/build/tasks/precompress.mjs +78 -78
  41. package/src/client/{cache-panel → admin}/i18n.js +756 -670
  42. package/src/client/{cache-panel → admin}/login.html +74 -74
  43. package/src/client/{cache-panel → admin}/panel.css +804 -756
  44. package/src/client/admin/panel.html +486 -0
  45. package/src/client/{cache-panel → admin}/panel.js +1242 -915
  46. package/src/client/devtools/report.html +185 -185
  47. package/src/client/devtools/report.js +725 -725
  48. package/src/client/dom.js +95 -95
  49. package/src/client/form.js +192 -192
  50. package/src/client/index.js +35 -35
  51. package/src/client/registry.js +297 -297
  52. package/src/client/safe-image.js +91 -91
  53. package/src/client/store.js +36 -36
  54. package/src/client/swap.js +188 -188
  55. package/src/config/defaults.js +16 -7
  56. package/src/config/index.js +38 -22
  57. package/src/config/pattern.js +107 -107
  58. package/src/http/control-flow.js +71 -71
  59. package/src/http/cookies.js +257 -257
  60. package/src/http/request-cache.js +46 -46
  61. package/src/http/request-context.js +162 -162
  62. package/src/index.js +83 -83
  63. package/src/init.mjs +221 -221
  64. package/src/log.mjs +58 -0
  65. package/src/runtime/alias-hooks.mjs +119 -119
  66. package/src/runtime/register.mjs +4 -4
  67. package/src/server/admin/actions.js +229 -0
  68. package/src/server/admin/auth.js +125 -0
  69. package/src/server/admin/event-log.js +151 -0
  70. package/src/server/admin/gate.js +209 -0
  71. package/src/server/admin/inventory.js +188 -0
  72. package/src/server/admin/mount.js +56 -0
  73. package/src/server/admin/router.js +216 -0
  74. package/src/server/admin/snapshot.js +126 -0
  75. package/src/server/assets.js +147 -147
  76. package/src/server/cache-deps.js +42 -42
  77. package/src/server/cloudflare.js +607 -607
  78. package/src/server/create-app.js +295 -291
  79. package/src/server/data-cache.js +462 -462
  80. package/src/server/dev/report.js +369 -369
  81. package/src/server/dev/socket.js +170 -170
  82. package/src/server/dev/version-check.mjs +139 -139
  83. package/src/server/html-cache.js +817 -817
  84. package/src/server/metadata.js +102 -102
  85. package/src/server/middleware/compression.js +205 -205
  86. package/src/server/middleware/csrf.js +134 -134
  87. package/src/server/middleware/dev-gate.js +62 -62
  88. package/src/server/middleware/headers.js +37 -37
  89. package/src/server/middleware/redirects.js +32 -32
  90. package/src/server/middleware/static-precompressed.js +100 -100
  91. package/src/server/middleware/trailing-slash.js +53 -0
  92. package/src/server/middleware/upstream-proxy.js +141 -141
  93. package/src/server/prewarm.js +601 -601
  94. package/src/server/redis.js +569 -569
  95. package/src/server/router.js +128 -128
  96. package/src/server/status-page.js +164 -164
  97. package/src/server/upstream-limiter.js +376 -376
  98. package/src/server/upstream-tracking.js +166 -166
  99. package/src/start.mjs +7 -7
  100. package/src/templates/layout.ejs +44 -44
  101. package/src/version.mjs +31 -31
  102. package/src/views/components/loader.js +85 -85
  103. package/src/views/helpers/html.js +102 -102
  104. package/src/views/helpers/tags.js +245 -245
  105. package/src/client/cache-panel/panel.html +0 -308
  106. package/src/server/cache-panel.js +0 -759
@@ -1,285 +1,285 @@
1
- # 01 — Başlangıç
2
-
3
- Bu belge JSkelet'i sıfırdan çalıştırmayı anlatır: paket kurulumu, `jskelet init`
4
- ile iskeletin oluşturulması, ilk route ve ilk island'ın yazılması, oluşan dizin
5
- yapısının ne anlama geldiği ve CLI'ın dört komutu. Sonunda tarayıcıda sunucuda
6
- render edilmiş, önbelleğe alınmış ve island'ı görünürlükte hidre olan bir sayfa
7
- olacak. Kararların *nedenleri* için [02-mimari.md](./02-mimari.md)'ye, buradaki
8
- her config alanının tam referansı için
9
- [07-yapilandirma.md](./07-yapilandirma.md)'ye bakın.
10
-
11
- ## Gereksinimler
12
-
13
- - **Node.js 22 veya üstü.** `package.json` → `engines` bunu zorunlu tutuyor.
14
- Framework `node:async_hooks`, `fs.readdirSync(..., { recursive: true })`,
15
- `--env-file-if-exists` ve `module.register()` gibi yeni Node yüzeylerini
16
- doğrudan kullanıyor.
17
- - Tailwind CSS kullanacaksanız `postcss`, `@tailwindcss/postcss` ve
18
- `tailwindcss` paketleri. Bunlar framework'ün **opsiyonel peer
19
- bağımlılıkları**dır; kurulu değilse CSS adımı atlanır ve site stilsiz ama
20
- çalışır durumda kalır (ayrıntı: [08-build.md](./08-build.md)).
21
-
22
- ## Kurulum
23
-
24
- ```bash
25
- mkdir benim-sitem && cd benim-sitem
26
- npm init -y
27
- npm pkg set type=module
28
- npm install jskelet
29
- npm install -D postcss @tailwindcss/postcss tailwindcss lightningcss
30
- ```
31
-
32
- `type: "module"` şart: route modülleri, bileşenler ve config dosyası ESM olarak
33
- yüklenir.
34
-
35
- Ardından `package.json` içine script'leri ekleyin:
36
-
37
- ```json
38
- {
39
- "scripts": {
40
- "dev": "jskelet dev",
41
- "build": "jskelet build",
42
- "start": "jskelet start"
43
- }
44
- }
45
- ```
46
-
47
- ## `jskelet init`
48
-
49
- ```bash
50
- npx jskelet init
51
- ```
52
-
53
- Bu komut bulunduğunuz dizine çalışan bir minimum iskelet kurar. **Var olan
54
- dosyaların üzerine yazmaz**: ikinci kez çalıştırmak yalnızca eksikleri
55
- tamamlar, atlanan dosyaların sayısını uyarı olarak basar. Amaç, "kurulumu
56
- yaptım ama hiçbir şey çalışmıyor" aşamasını tamamen atlamak — `jskelet dev`
57
- hemen ardından çalışır.
58
-
59
- Oluşturulan dosyalar:
60
-
61
- ```
62
- jskelet.config.mjs config: brand, preconnect, cache(), hooks
63
- routes/10-pages.mjs "/" route'u
64
- views/pages/home.ejs ana sayfa şablonu
65
- views/pages/not-found.ejs 404 şablonu
66
- views/components/button.js örnek bileşen (HTML string döndüren fonksiyon)
67
- client/entries/main.js island bootstrap'ı
68
- client/islands/counter.js örnek island
69
- styles/globals.css Tailwind girişi + @source direktifleri
70
- jsconfig.json checkJs + "@/*" alias'ı
71
- .gitignore node_modules/, .jskelet/, public/assets/, .env
72
- ```
73
-
74
- Sonra:
75
-
76
- ```bash
77
- npm run dev
78
- ```
79
-
80
- Terminalde banner, hizalı build satırları ve bir `Ready` özeti görürsünüz;
81
- `http://localhost:3000` sayfayı verir. Sağ altta dev overlay baloncuğu durur,
82
- `Alt+D` ile açılır ([09-dev-araclari.md](./09-dev-araclari.md)).
83
-
84
- ## Dizin yapısı
85
-
86
- Dizin adlarının hiçbiri sabit değildir; hepsi `jskelet.config.mjs` → `paths`
87
- ile ezilebilir. Aşağıdaki değerler varsayılanlardır (`src/config/defaults.js`).
88
-
89
- | Dizin | Varsayılan | İçeriği |
90
- | --- | --- | --- |
91
- | `views` | `views` | EJS layout, sayfalar ve bileşenler |
92
- | `public` | `public` | Statik dosyalar; build çıktısı da buraya yazılır |
93
- | `client` | `client` | Island runtime kaynakları ve entry'ler |
94
- | `routes` | `routes` | Route modülleri |
95
- | `styles` | `styles/globals.css` | Tailwind/PostCSS giriş **dosyası** |
96
- | `generated` | `.jskelet` | Build ara çıktıları: `manifest.json`, `metafile.json`, `images.json` |
97
-
98
- Bunlara ek olarak framework iki yolu her zaman türetir ve ayrı ayar kabul
99
- etmez: `public/assets` (hash'li build çıktısı) ve `public/fonts` (self-host
100
- fontlar).
101
-
102
- Tipik bir proje:
103
-
104
- ```
105
- benim-sitem/
106
- ├── jskelet.config.mjs
107
- ├── jsconfig.json
108
- ├── routes/
109
- │ ├── 10-pages.mjs
110
- │ └── 90-catch-all.mjs
111
- ├── views/
112
- │ ├── layout.ejs
113
- │ ├── pages/
114
- │ │ ├── home.ejs
115
- │ │ └── not-found.ejs
116
- │ └── components/
117
- │ └── card.js
118
- ├── client/
119
- │ ├── entries/
120
- │ │ └── main.js
121
- │ └── islands/
122
- │ └── counter.js
123
- ├── styles/
124
- │ └── globals.css
125
- ├── public/
126
- │ └── (statik dosyalar; build → public/assets)
127
- └── .jskelet/
128
- └── manifest.json
129
- ```
130
-
131
- ## İlk route
132
-
133
- Route modülleri **dosya sistemine dayalı otomatik URL türetmez**; her modül
134
- kendi yollarını `app.get(...)` ile açıkça yazar. Modül sözleşmesi: default
135
- export ya da `register` adlı named export, `(app, api)` imzasıyla.
136
-
137
- ```js
138
- // routes/10-pages.mjs
139
- export default function register(app, { route }) {
140
- app.get(
141
- "/",
142
- route(
143
- async () => ({
144
- view: "pages/home",
145
- metadata: { title: "Ana sayfa" },
146
- data: { heading: "JSkelet çalışıyor", items: ["Bir", "İki"] },
147
- }),
148
- { revalidate: 60 },
149
- ),
150
- );
151
- }
152
- ```
153
-
154
- `api` nesnesi içinde `route`, `renderView`, `renderPage`, `notFound`, `redirect`
155
- ve `permanentRedirect` hazır gelir; route dosyaları framework'ten tek tek import
156
- yapmak zorunda kalmaz. `route()` controller'ı sarar: HTML cache'i,
157
- notFound/redirect kontrol akışı, sıkıştırma ve `X-JSkelet-Cache` başlığı ondan
158
- gelir. Controller'ın tek işi bir sayfa tanımı döndürmektir.
159
-
160
- Dosya adındaki `10-` öneki yükleme sırasını belirler. `routes/` alfabetik
161
- tarandığı için `/:slug` gibi yakalayıcı route'ları daha yüksek numaralı bir
162
- dosyaya koymalısınız; aksi hâlde `/hakkinda` bir slug sanılır. Ayrıntı:
163
- [03-routing.md](./03-routing.md).
164
-
165
- Şablon tarafı düz EJS:
166
-
167
- ```ejs
168
- <%# views/pages/home.ejs %>
169
- <section class="wrapper">
170
- <h1 class="text-3xl font-bold"><%= heading %></h1>
171
- <%- list({ items }) %>
172
- <div data-island="counter" data-island-props='{"start":5}'></div>
173
- </section>
174
- ```
175
-
176
- `list` burada `views/components/list.js` içinde tanımlı bir fonksiyondur ve
177
- import edilmemiştir: `views/components/**` altındaki her named export otomatik
178
- olarak şablon local'i olur ([04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md)).
179
-
180
- ## İlk island
181
-
182
- Island, sunucunun ürettiği HTML'e davranış ekleyen küçük bir modüldür. Sözleşme
183
- iki parçadan oluşur.
184
-
185
- **1. Şablonda işaret:** bir elemente `data-island="ad"` verin. Props JSON olarak
186
- `data-island-props` içinde taşınır.
187
-
188
- ```ejs
189
- <div data-island="counter" data-island-props='{"start":5}'></div>
190
- ```
191
-
192
- **2. Modülde `mount`:** island `mount(element, props)` adlı bir named export
193
- verir.
194
-
195
- ```js
196
- // client/islands/counter.js
197
- /**
198
- * @param {HTMLElement} element
199
- * @param {{ start?: number }} props
200
- */
201
- export function mount(element, props) {
202
- let value = props.start ?? 0;
203
-
204
- const button = document.createElement("button");
205
- button.type = "button";
206
-
207
- const paint = () => {
208
- button.textContent = `Tıklama: ${value}`;
209
- };
210
-
211
- button.addEventListener("click", () => {
212
- value += 1;
213
- paint();
214
- });
215
-
216
- paint();
217
- element.append(button);
218
- }
219
- ```
220
-
221
- **3. Kayıt:** `client/entries/main.js` island adını dinamik import'a bağlar ve
222
- runtime'ı başlatır.
223
-
224
- ```js
225
- import { registerAll, start } from "jskelet/client";
226
-
227
- registerAll({
228
- counter: () => import("../islands/counter.js"),
229
- });
230
-
231
- start();
232
- ```
233
-
234
- Değerlerin dinamik import olması kritik: modül yalnızca sayfada o island
235
- gerçekten varsa **ve** element görünür hâle geldiğinde indirilir. Yani bu
236
- haritayı büyütmek ilk yükü büyütmez. Hidrasyon stratejileri
237
- (`data-island-eager`, `data-island-idle`) ve runtime API'sinin tamamı
238
- [05-islands.md](./05-islands.md)'de.
239
-
240
- ## CLI komutları
241
-
242
- `bin/jskelet.mjs` dört alt komut sunar. Her biri ayrı bir Node sürecinde
243
- çalışır; sebebi `dev`in iki uzun ömürlü süreci yönetmesi ve sunucunun ESM
244
- resolve hook'larına (`--import`) süreç başlangıcında ihtiyaç duyması.
245
-
246
- | Komut | Ne yapar |
247
- | --- | --- |
248
- | `jskelet dev` | Build watch + sunucu, tek terminalde. Canlı yenileme, CSS hot-swap, dev overlay. `NODE_ENV=development`. |
249
- | `jskelet build` | Tek seferlik prod build: fontlar → ikon sprite → CSS → client JS → görseller → manifest → precompress. `NODE_ENV` verilmemişse `production`. |
250
- | `jskelet start` | Prod sunucu. Build çıktısı yoksa önce üretir. `NODE_ENV` verilmemişse `production`. |
251
- | `jskelet init` | Bulunduğun dizine minimal iskelet kurar; var olan dosyalara dokunmaz. |
252
-
253
- Bilinmeyen bir komut ya da argümansız çağrı kullanım metnini basar.
254
-
255
- Her komut iki Node bayrağıyla çalışır:
256
-
257
- - `--env-file=.env` — yalnızca dosya gerçekten varsa geçilir; yoksa hiçbir
258
- bayrak eklenmez ve uyarı basılmaz.
259
- - `--import <register.mjs>` — `jsconfig.json` / `tsconfig.json` içindeki
260
- `compilerOptions.paths` alias'larını (`@/lib/x`) ve uzantısız göreli
261
- import'ları (`./cache` → `./cache.js`) çözen ESM hook'larını kurar.
262
- (`jskelet dev` bu hook'ları kendi alt süreçlerinde kurar, dış süreçte kurmaz.)
263
-
264
- ## İthal yolları
265
-
266
- `package.json` → `exports` haritası kararlı yüzeyi tanımlar. Örneklerde
267
- yalnızca bu belirteçleri kullanın:
268
-
269
- | Belirteç | İçeriği |
270
- | --- | --- |
271
- | `jskelet` | Sunucu API'si: `route`, `renderPage`, `renderView`, `renderNotFound`, `createApp`, `startServer`, `notFound`, `redirect`, `permanentRedirect`, `cache`, `withRequestCache`, `reportUpstreamFailure`, `asset`, `hasAsset`, `optimizedImage`, `getSpriteIds`, `headHints`, `renderHeadMeta`, HTML cache fonksiyonları, `prewarm`, `createProxy`, `getConfig`, `loadConfig` ve html/tag yardımcıları |
272
- | `jskelet/server` | `jskelet` ile aynı modül (okunurluk için takma ad) |
273
- | `jskelet/client` | Tarayıcı runtime'ı: `register`, `registerAll`, `hydrate`, `observeDocument`, `start`, `createStore`, DOM yardımcıları, `startSafeImages` |
274
- | `jskelet/html` | `esc`, `attrs`, `cx`, `cn`, `jsonScript` |
275
- | `jskelet/tags` | `link`, `image`, `icon`, `preloadImage`, `toKebab` |
276
- | `jskelet/log` | Konsol çıktısı yardımcıları (`banner`, `event`, `task`, `size`, `ms`, …) |
277
- | `jskelet/register` | `node --import jskelet/register` ile alias + uzantı hook'ları |
278
- | `jskelet/layout` | Framework'ün varsayılan `layout.ejs` dosyasının yolu |
279
-
280
- ## Sırada ne var
281
-
282
- - Neden bu şekilde çalışıyor: [02-mimari.md](./02-mimari.md)
283
- - Daha fazla route ve yakalayıcı desenler: [03-routing.md](./03-routing.md)
284
- - Layout'u devralmak ve metadata: [04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md)
285
- - Önbelleği ayarlamak: [06-cache.md](./06-cache.md)
1
+ # 01 — Başlangıç
2
+
3
+ Bu belge JSkelet'i sıfırdan çalıştırmayı anlatır: paket kurulumu, `jskelet init`
4
+ ile iskeletin oluşturulması, ilk route ve ilk island'ın yazılması, oluşan dizin
5
+ yapısının ne anlama geldiği ve CLI'ın dört komutu. Sonunda tarayıcıda sunucuda
6
+ render edilmiş, önbelleğe alınmış ve island'ı görünürlükte hidre olan bir sayfa
7
+ olacak. Kararların *nedenleri* için [02-mimari.md](./02-mimari.md)'ye, buradaki
8
+ her config alanının tam referansı için
9
+ [07-yapilandirma.md](./07-yapilandirma.md)'ye bakın.
10
+
11
+ ## Gereksinimler
12
+
13
+ - **Node.js 22 veya üstü.** `package.json` → `engines` bunu zorunlu tutuyor.
14
+ Framework `node:async_hooks`, `fs.readdirSync(..., { recursive: true })`,
15
+ `--env-file-if-exists` ve `module.register()` gibi yeni Node yüzeylerini
16
+ doğrudan kullanıyor.
17
+ - Tailwind CSS kullanacaksanız `postcss`, `@tailwindcss/postcss` ve
18
+ `tailwindcss` paketleri. Bunlar framework'ün **opsiyonel peer
19
+ bağımlılıkları**dır; kurulu değilse CSS adımı atlanır ve site stilsiz ama
20
+ çalışır durumda kalır (ayrıntı: [08-build.md](./08-build.md)).
21
+
22
+ ## Kurulum
23
+
24
+ ```bash
25
+ mkdir benim-sitem && cd benim-sitem
26
+ npm init -y
27
+ npm pkg set type=module
28
+ npm install jskelet
29
+ npm install -D postcss @tailwindcss/postcss tailwindcss lightningcss
30
+ ```
31
+
32
+ `type: "module"` şart: route modülleri, bileşenler ve config dosyası ESM olarak
33
+ yüklenir.
34
+
35
+ Ardından `package.json` içine script'leri ekleyin:
36
+
37
+ ```json
38
+ {
39
+ "scripts": {
40
+ "dev": "jskelet dev",
41
+ "build": "jskelet build",
42
+ "start": "jskelet start"
43
+ }
44
+ }
45
+ ```
46
+
47
+ ## `jskelet init`
48
+
49
+ ```bash
50
+ npx jskelet init
51
+ ```
52
+
53
+ Bu komut bulunduğunuz dizine çalışan bir minimum iskelet kurar. **Var olan
54
+ dosyaların üzerine yazmaz**: ikinci kez çalıştırmak yalnızca eksikleri
55
+ tamamlar, atlanan dosyaların sayısını uyarı olarak basar. Amaç, "kurulumu
56
+ yaptım ama hiçbir şey çalışmıyor" aşamasını tamamen atlamak — `jskelet dev`
57
+ hemen ardından çalışır.
58
+
59
+ Oluşturulan dosyalar:
60
+
61
+ ```
62
+ jskelet.config.mjs config: brand, preconnect, cache(), hooks
63
+ routes/10-pages.mjs "/" route'u
64
+ views/pages/home.ejs ana sayfa şablonu
65
+ views/pages/not-found.ejs 404 şablonu
66
+ views/components/button.js örnek bileşen (HTML string döndüren fonksiyon)
67
+ client/entries/main.js island bootstrap'ı
68
+ client/islands/counter.js örnek island
69
+ styles/globals.css Tailwind girişi + @source direktifleri
70
+ jsconfig.json checkJs + "@/*" alias'ı
71
+ .gitignore node_modules/, .jskelet/, public/assets/, .env
72
+ ```
73
+
74
+ Sonra:
75
+
76
+ ```bash
77
+ npm run dev
78
+ ```
79
+
80
+ Terminalde banner, hizalı build satırları ve bir `Ready` özeti görürsünüz;
81
+ `http://localhost:3000` sayfayı verir. Sağ altta dev overlay baloncuğu durur,
82
+ `Alt+D` ile açılır ([09-dev-araclari.md](./09-dev-araclari.md)).
83
+
84
+ ## Dizin yapısı
85
+
86
+ Dizin adlarının hiçbiri sabit değildir; hepsi `jskelet.config.mjs` → `paths`
87
+ ile ezilebilir. Aşağıdaki değerler varsayılanlardır (`src/config/defaults.js`).
88
+
89
+ | Dizin | Varsayılan | İçeriği |
90
+ | --- | --- | --- |
91
+ | `views` | `views` | EJS layout, sayfalar ve bileşenler |
92
+ | `public` | `public` | Statik dosyalar; build çıktısı da buraya yazılır |
93
+ | `client` | `client` | Island runtime kaynakları ve entry'ler |
94
+ | `routes` | `routes` | Route modülleri |
95
+ | `styles` | `styles/globals.css` | Tailwind/PostCSS giriş **dosyası** |
96
+ | `generated` | `.jskelet` | Build ara çıktıları: `manifest.json`, `metafile.json`, `images.json` |
97
+
98
+ Bunlara ek olarak framework iki yolu her zaman türetir ve ayrı ayar kabul
99
+ etmez: `public/assets` (hash'li build çıktısı) ve `public/fonts` (self-host
100
+ fontlar).
101
+
102
+ Tipik bir proje:
103
+
104
+ ```
105
+ benim-sitem/
106
+ ├── jskelet.config.mjs
107
+ ├── jsconfig.json
108
+ ├── routes/
109
+ │ ├── 10-pages.mjs
110
+ │ └── 90-catch-all.mjs
111
+ ├── views/
112
+ │ ├── layout.ejs
113
+ │ ├── pages/
114
+ │ │ ├── home.ejs
115
+ │ │ └── not-found.ejs
116
+ │ └── components/
117
+ │ └── card.js
118
+ ├── client/
119
+ │ ├── entries/
120
+ │ │ └── main.js
121
+ │ └── islands/
122
+ │ └── counter.js
123
+ ├── styles/
124
+ │ └── globals.css
125
+ ├── public/
126
+ │ └── (statik dosyalar; build → public/assets)
127
+ └── .jskelet/
128
+ └── manifest.json
129
+ ```
130
+
131
+ ## İlk route
132
+
133
+ Route modülleri **dosya sistemine dayalı otomatik URL türetmez**; her modül
134
+ kendi yollarını `app.get(...)` ile açıkça yazar. Modül sözleşmesi: default
135
+ export ya da `register` adlı named export, `(app, api)` imzasıyla.
136
+
137
+ ```js
138
+ // routes/10-pages.mjs
139
+ export default function register(app, { route }) {
140
+ app.get(
141
+ "/",
142
+ route(
143
+ async () => ({
144
+ view: "pages/home",
145
+ metadata: { title: "Ana sayfa" },
146
+ data: { heading: "JSkelet çalışıyor", items: ["Bir", "İki"] },
147
+ }),
148
+ { revalidate: 60 },
149
+ ),
150
+ );
151
+ }
152
+ ```
153
+
154
+ `api` nesnesi içinde `route`, `renderView`, `renderPage`, `notFound`, `redirect`
155
+ ve `permanentRedirect` hazır gelir; route dosyaları framework'ten tek tek import
156
+ yapmak zorunda kalmaz. `route()` controller'ı sarar: HTML cache'i,
157
+ notFound/redirect kontrol akışı, sıkıştırma ve `X-JSkelet-Cache` başlığı ondan
158
+ gelir. Controller'ın tek işi bir sayfa tanımı döndürmektir.
159
+
160
+ Dosya adındaki `10-` öneki yükleme sırasını belirler. `routes/` alfabetik
161
+ tarandığı için `/:slug` gibi yakalayıcı route'ları daha yüksek numaralı bir
162
+ dosyaya koymalısınız; aksi hâlde `/hakkinda` bir slug sanılır. Ayrıntı:
163
+ [03-routing.md](./03-routing.md).
164
+
165
+ Şablon tarafı düz EJS:
166
+
167
+ ```ejs
168
+ <%# views/pages/home.ejs %>
169
+ <section class="wrapper">
170
+ <h1 class="text-3xl font-bold"><%= heading %></h1>
171
+ <%- list({ items }) %>
172
+ <div data-island="counter" data-island-props='{"start":5}'></div>
173
+ </section>
174
+ ```
175
+
176
+ `list` burada `views/components/list.js` içinde tanımlı bir fonksiyondur ve
177
+ import edilmemiştir: `views/components/**` altındaki her named export otomatik
178
+ olarak şablon local'i olur ([04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md)).
179
+
180
+ ## İlk island
181
+
182
+ Island, sunucunun ürettiği HTML'e davranış ekleyen küçük bir modüldür. Sözleşme
183
+ iki parçadan oluşur.
184
+
185
+ **1. Şablonda işaret:** bir elemente `data-island="ad"` verin. Props JSON olarak
186
+ `data-island-props` içinde taşınır.
187
+
188
+ ```ejs
189
+ <div data-island="counter" data-island-props='{"start":5}'></div>
190
+ ```
191
+
192
+ **2. Modülde `mount`:** island `mount(element, props)` adlı bir named export
193
+ verir.
194
+
195
+ ```js
196
+ // client/islands/counter.js
197
+ /**
198
+ * @param {HTMLElement} element
199
+ * @param {{ start?: number }} props
200
+ */
201
+ export function mount(element, props) {
202
+ let value = props.start ?? 0;
203
+
204
+ const button = document.createElement("button");
205
+ button.type = "button";
206
+
207
+ const paint = () => {
208
+ button.textContent = `Tıklama: ${value}`;
209
+ };
210
+
211
+ button.addEventListener("click", () => {
212
+ value += 1;
213
+ paint();
214
+ });
215
+
216
+ paint();
217
+ element.append(button);
218
+ }
219
+ ```
220
+
221
+ **3. Kayıt:** `client/entries/main.js` island adını dinamik import'a bağlar ve
222
+ runtime'ı başlatır.
223
+
224
+ ```js
225
+ import { registerAll, start } from "jskelet/client";
226
+
227
+ registerAll({
228
+ counter: () => import("../islands/counter.js"),
229
+ });
230
+
231
+ start();
232
+ ```
233
+
234
+ Değerlerin dinamik import olması kritik: modül yalnızca sayfada o island
235
+ gerçekten varsa **ve** element görünür hâle geldiğinde indirilir. Yani bu
236
+ haritayı büyütmek ilk yükü büyütmez. Hidrasyon stratejileri
237
+ (`data-island-eager`, `data-island-idle`) ve runtime API'sinin tamamı
238
+ [05-islands.md](./05-islands.md)'de.
239
+
240
+ ## CLI komutları
241
+
242
+ `bin/jskelet.mjs` dört alt komut sunar. Her biri ayrı bir Node sürecinde
243
+ çalışır; sebebi `dev`in iki uzun ömürlü süreci yönetmesi ve sunucunun ESM
244
+ resolve hook'larına (`--import`) süreç başlangıcında ihtiyaç duyması.
245
+
246
+ | Komut | Ne yapar |
247
+ | --- | --- |
248
+ | `jskelet dev` | Build watch + sunucu, tek terminalde. Canlı yenileme, CSS hot-swap, dev overlay. `NODE_ENV=development`. |
249
+ | `jskelet build` | Tek seferlik prod build: fontlar → ikon sprite → CSS → client JS → görseller → manifest → precompress. `NODE_ENV` verilmemişse `production`. |
250
+ | `jskelet start` | Prod sunucu. Build çıktısı yoksa önce üretir. `NODE_ENV` verilmemişse `production`. |
251
+ | `jskelet init` | Bulunduğun dizine minimal iskelet kurar; var olan dosyalara dokunmaz. |
252
+
253
+ Bilinmeyen bir komut ya da argümansız çağrı kullanım metnini basar.
254
+
255
+ Her komut iki Node bayrağıyla çalışır:
256
+
257
+ - `--env-file=.env` — yalnızca dosya gerçekten varsa geçilir; yoksa hiçbir
258
+ bayrak eklenmez ve uyarı basılmaz.
259
+ - `--import <register.mjs>` — `jsconfig.json` / `tsconfig.json` içindeki
260
+ `compilerOptions.paths` alias'larını (`@/lib/x`) ve uzantısız göreli
261
+ import'ları (`./cache` → `./cache.js`) çözen ESM hook'larını kurar.
262
+ (`jskelet dev` bu hook'ları kendi alt süreçlerinde kurar, dış süreçte kurmaz.)
263
+
264
+ ## İthal yolları
265
+
266
+ `package.json` → `exports` haritası kararlı yüzeyi tanımlar. Örneklerde
267
+ yalnızca bu belirteçleri kullanın:
268
+
269
+ | Belirteç | İçeriği |
270
+ | --- | --- |
271
+ | `jskelet` | Sunucu API'si: `route`, `renderPage`, `renderView`, `renderNotFound`, `createApp`, `startServer`, `notFound`, `redirect`, `permanentRedirect`, `cache`, `withRequestCache`, `reportUpstreamFailure`, `asset`, `hasAsset`, `optimizedImage`, `getSpriteIds`, `headHints`, `renderHeadMeta`, HTML cache fonksiyonları, `prewarm`, `createProxy`, `getConfig`, `loadConfig` ve html/tag yardımcıları |
272
+ | `jskelet/server` | `jskelet` ile aynı modül (okunurluk için takma ad) |
273
+ | `jskelet/client` | Tarayıcı runtime'ı: `register`, `registerAll`, `hydrate`, `observeDocument`, `start`, `createStore`, DOM yardımcıları, `startSafeImages` |
274
+ | `jskelet/html` | `esc`, `attrs`, `cx`, `cn`, `jsonScript` |
275
+ | `jskelet/tags` | `link`, `image`, `icon`, `preloadImage`, `toKebab` |
276
+ | `jskelet/log` | Konsol çıktısı yardımcıları (`banner`, `event`, `task`, `size`, `ms`, …) |
277
+ | `jskelet/register` | `node --import jskelet/register` ile alias + uzantı hook'ları |
278
+ | `jskelet/layout` | Framework'ün varsayılan `layout.ejs` dosyasının yolu |
279
+
280
+ ## Sırada ne var
281
+
282
+ - Neden bu şekilde çalışıyor: [02-mimari.md](./02-mimari.md)
283
+ - Daha fazla route ve yakalayıcı desenler: [03-routing.md](./03-routing.md)
284
+ - Layout'u devralmak ve metadata: [04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md)
285
+ - Önbelleği ayarlamak: [06-cache.md](./06-cache.md)