jskelet 0.5.5 → 0.6.1
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.
- package/AGENTS.md +19 -15
- package/CHANGELOG.md +165 -15
- package/README.md +16 -21
- package/bin/jskelet.mjs +23 -9
- package/docs/01-baslangic.md +4 -3
- package/docs/02-mimari.md +10 -4
- package/docs/03-routing.md +14 -7
- package/docs/04-render-ve-sablonlar.md +60 -43
- package/docs/05-islands.md +12 -8
- package/docs/06-cache.md +18 -7
- package/docs/07-yapilandirma.md +69 -27
- package/docs/08-build.md +15 -9
- package/docs/09-dev-araclari.md +22 -8
- package/docs/10-dagitim.md +14 -13
- package/docs/11-tasima.md +51 -17
- package/docs/12-panel-ve-oturum.md +10 -4
- package/docs/README.md +10 -33
- package/docs/en/01-getting-started.md +4 -3
- package/docs/en/02-architecture.md +12 -6
- package/docs/en/03-routing.md +15 -8
- package/docs/en/04-rendering.md +71 -59
- package/docs/en/05-islands.md +13 -8
- package/docs/en/06-caching.md +21 -7
- package/docs/en/07-configuration.md +69 -29
- package/docs/en/08-build.md +16 -10
- package/docs/en/09-dev-tools.md +24 -8
- package/docs/en/10-deployment.md +14 -14
- package/docs/en/11-migration.md +51 -16
- package/docs/en/12-dashboards-and-sessions.md +9 -4
- package/docs/en/README.md +10 -35
- package/package.json +48 -13
- package/src/build/tasks/client.mjs +91 -10
- package/src/build/tasks/icons.mjs +11 -1
- package/src/client/index.js +2 -2
- package/src/compile/codegen.js +4 -0
- package/src/compile/compile-all.js +12 -21
- package/src/compile/expr.js +5 -0
- package/src/compile/parse.js +64 -8
- package/src/compile/resolve.js +3 -0
- package/src/config/defaults.js +48 -5
- package/src/config/index.js +138 -27
- package/src/dev-server.mjs +26 -3
- package/src/http/cookies-entry.js +1 -0
- package/src/http/cookies.js +18 -0
- package/src/logo.png +0 -0
- package/src/migrate/apply.mjs +262 -0
- package/src/migrate/babel.mjs +79 -0
- package/src/migrate/classify.mjs +155 -0
- package/src/migrate/config.mjs +126 -0
- package/src/migrate/fs-walk.mjs +191 -0
- package/src/migrate/parse.mjs +26 -0
- package/src/migrate/scan.mjs +177 -0
- package/src/migrate/transform/expr-source.mjs +168 -0
- package/src/migrate/transform/island.mjs +67 -0
- package/src/migrate/transform/jsx-to-component.mjs +302 -0
- package/src/migrate/transform/jsx-to-jsk.mjs +330 -0
- package/src/migrate/transform/page-split.mjs +435 -0
- package/src/migrate/write.mjs +81 -0
- package/src/migrate.mjs +171 -0
- package/src/server/auth/handoff.js +94 -11
- package/src/server/create-app.js +37 -10
- package/src/server/ejs-adapter.js +59 -0
- package/src/server/html-cache.js +178 -32
- package/src/server/image-optimizer.js +94 -26
- package/src/server/middleware/dev-gate.js +21 -8
- package/src/server/middleware/robots-txt.js +341 -0
- package/src/server/port-guard.js +255 -0
- package/src/server/prewarm.js +137 -51
- package/src/server/render.js +30 -10
- package/src/server/status-page.js +105 -4
- package/src/start.mjs +18 -3
- package/src/templates/layout.ejs +8 -28
- package/src/templates/layout.jsk +30 -0
- package/src/templates/layout.render.js +41 -0
- package/src/views/helpers/tags.js +86 -3
- package/types/build/resolve-peer.d.mts +13 -0
- package/types/client/dom.d.ts +55 -0
- package/types/client/form.d.ts +19 -0
- package/types/client/index.d.ts +20 -0
- package/types/client/registry.d.ts +53 -0
- package/types/client/safe-image.d.ts +19 -0
- package/types/client/shared-cookie.d.ts +82 -0
- package/types/client/store.d.ts +18 -0
- package/types/client/swap.d.ts +46 -0
- package/types/compile/codegen.d.ts +32 -0
- package/types/compile/compile-all.d.ts +42 -0
- package/types/compile/errors.d.ts +30 -0
- package/types/compile/expr.d.ts +67 -0
- package/types/compile/index.d.ts +10 -0
- package/types/compile/parse.d.ts +82 -0
- package/types/compile/resolve.d.ts +46 -0
- package/types/compile/scan-exports.d.ts +9 -0
- package/types/config/defaults.d.ts +477 -0
- package/types/config/index.d.ts +304 -0
- package/types/config/pattern.d.ts +38 -0
- package/types/http/control-flow.d.ts +45 -0
- package/types/http/cookies-entry.d.ts +5 -0
- package/types/http/cookies.d.ts +113 -0
- package/types/http/request-cache.d.ts +13 -0
- package/types/http/request-context.d.ts +67 -0
- package/types/http/shared-cookie.d.ts +73 -0
- package/types/index.d.ts +30 -0
- package/types/log.d.mts +153 -0
- package/types/server/admin/actions.d.ts +16 -0
- package/types/server/admin/auth.d.ts +52 -0
- package/types/server/admin/event-log.d.ts +38 -0
- package/types/server/admin/gate.d.ts +43 -0
- package/types/server/admin/inventory.d.ts +40 -0
- package/types/server/admin/mount.d.ts +6 -0
- package/types/server/admin/router.d.ts +6 -0
- package/types/server/admin/snapshot.d.ts +6 -0
- package/types/server/assets.d.ts +47 -0
- package/types/server/auth/handoff.d.ts +12 -0
- package/types/server/cache-deps.d.ts +16 -0
- package/types/server/cache-vary.d.ts +30 -0
- package/types/server/cloudflare.d.ts +163 -0
- package/types/server/create-app.d.ts +25 -0
- package/types/server/data-cache.d.ts +116 -0
- package/types/server/dev/devtools.d.ts +44 -0
- package/types/server/dev/report.d.ts +229 -0
- package/types/server/dev/socket.d.ts +17 -0
- package/types/server/dev/version-check.d.mts +15 -0
- package/types/server/ejs-adapter.d.ts +11 -0
- package/types/server/head-hints.d.ts +40 -0
- package/types/server/html-cache.d.ts +207 -0
- package/types/server/image-optimizer.d.ts +68 -0
- package/types/server/logs/access-middleware.d.ts +7 -0
- package/types/server/logs/file-sink.d.ts +17 -0
- package/types/server/logs/pipeline.d.ts +37 -0
- package/types/server/logs/s3-put.d.ts +85 -0
- package/types/server/logs/s3-sink.d.ts +26 -0
- package/types/server/metadata.d.ts +38 -0
- package/types/server/middleware/compression.d.ts +17 -0
- package/types/server/middleware/csrf.d.ts +4 -0
- package/types/server/middleware/dev-gate.d.ts +2 -0
- package/types/server/middleware/headers.d.ts +2 -0
- package/types/server/middleware/redirects.d.ts +2 -0
- package/types/server/middleware/robots-txt.d.ts +33 -0
- package/types/server/middleware/static-precompressed.d.ts +5 -0
- package/types/server/middleware/trailing-slash.d.ts +11 -0
- package/types/server/middleware/upstream-proxy.d.ts +21 -0
- package/types/server/og-image.d.ts +149 -0
- package/types/server/port-guard.d.ts +50 -0
- package/types/server/prewarm.d.ts +131 -0
- package/types/server/redis.d.ts +163 -0
- package/types/server/render.d.ts +101 -0
- package/types/server/router.d.ts +5 -0
- package/types/server/status-page.d.ts +24 -0
- package/types/server/upstream-limiter.d.ts +123 -0
- package/types/server/upstream-tracking.d.ts +42 -0
- package/types/shared/cookie-domain.d.ts +29 -0
- package/types/templates/layout.render.d.ts +7 -0
- package/types/version.d.mts +10 -0
- package/types/views/components/loader.d.ts +5 -0
- package/types/views/helpers/html.d.ts +39 -0
- package/types/views/helpers/tags.d.ts +127 -0
package/docs/10-dagitim.md
CHANGED
|
@@ -23,6 +23,10 @@ kendisi çalıştırır. Docker imajında build zaten yapıldığı için bu bir
|
|
|
23
23
|
amaç `npm start`ı doğrudan çalıştıran birinin stilsiz bir sayfayla
|
|
24
24
|
karşılaşmaması.
|
|
25
25
|
|
|
26
|
+
Port doluysa süreç **başlamaz** (PID + ipucu). `jskelet start --murder` o
|
|
27
|
+
porttaki dinleyiciyi öldürüp bağlar — geliştirmede unutulmuş bir süreç için;
|
|
28
|
+
üretim orkestratöründe genelde gerekmez.
|
|
29
|
+
|
|
26
30
|
Sunucu hazır olduğunda tek satır basar:
|
|
27
31
|
|
|
28
32
|
```
|
|
@@ -46,7 +50,7 @@ ayarlamayı düşünmeniz gerekenler:
|
|
|
46
50
|
| `HOST` | `0.0.0.0` | Yalnızca IPv4 dinlemek gerekiyorsa. Varsayılan `::` zaten çift yığın dinler |
|
|
47
51
|
| `PREWARM_MAX` | Site boyutuna göre | Açılışta ısıtılacak sayfa sayısı |
|
|
48
52
|
| `PREWARM_INTERVAL_SECONDS` | `0` ya da uzun bir değer | Hiç ziyaret edilmeyen sayfaları sıcak tutmak isterseniz |
|
|
49
|
-
| `DEV_TOKEN` | Yalnızca staging'de | Yayına açılmamış ortamı gizler |
|
|
53
|
+
| `DEV_GATE` + `DEV_TOKEN` | Yalnızca staging'de | Yayına açılmamış ortamı gizler. Token tek başına siteyi kilitlemez |
|
|
50
54
|
| `JSKELET_S3_*` | Access log'u S3'e yazıyorsanız | Bucket + credential; ayrıntı [07](./07-yapilandirma.md) |
|
|
51
55
|
|
|
52
56
|
Production'da dosya veya S3 sink açıldığında HTTP access log middleware
|
|
@@ -63,7 +67,8 @@ değerin geçerli olduğunu belirsizleştirir; prod imajında `.env` bulundurmam
|
|
|
63
67
|
temizidir.
|
|
64
68
|
|
|
65
69
|
**Gizli anahtarlar `clientEnv` listesine konmamalıdır:** oradaki değerler client
|
|
66
|
-
bundle'a düz metin olarak gömülür ([08-build.md](./08-build.md)).
|
|
70
|
+
bundle'a düz metin olarak gömülür ([08-build.md](./08-build.md)). Secret benzeri
|
|
71
|
+
isimler (`SECRET`, `API_KEY`, …) artık build'i düşürür.
|
|
67
72
|
|
|
68
73
|
## Docker
|
|
69
74
|
|
|
@@ -151,15 +156,11 @@ Build aşaması `npx jskelet build` ile bunları kendisi üretir.
|
|
|
151
156
|
|
|
152
157
|
Bu depodaki örnekler jskelet'i npm'den değil `"jskelet": "file:../.."` ile
|
|
153
158
|
alıyor. Coolify, Railway, Render gibi araçlarda "base directory" olarak
|
|
154
|
-
`examples/
|
|
159
|
+
`examples/blog` verilirse build context yalnızca o dizin olur, `../..`
|
|
155
160
|
context'in dışında kalır ve kurulum `npm ci`de düşer. Doğru ayar: **base
|
|
156
|
-
directory
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
```bash
|
|
160
|
-
docker build -f examples/marketing/Dockerfile -t jskelet-marketing .
|
|
161
|
-
docker run --rm -p 3000:3000 -e SITE_URL=https://example.com jskelet-marketing
|
|
162
|
-
```
|
|
161
|
+
directory `/`** (depo kökü) ve imajı yukarıdaki çok aşamalı Dockerfile ile
|
|
162
|
+
uygulama dizinine göre uyarlamak — ya da jskelet'i npm bağımlılığı olarak
|
|
163
|
+
kurup context'i uygulamanın kendi dizini yapmak.
|
|
163
164
|
|
|
164
165
|
Kendi uygulamanızda jskelet normal bir bağımlılık olacağı için bu kısıt yoktur;
|
|
165
166
|
yukarıdaki çok aşamalı imaj yeterli.
|
|
@@ -168,8 +169,8 @@ yukarıdaki çok aşamalı imaj yeterli.
|
|
|
168
169
|
|
|
169
170
|
Framework hazır bir sağlık kontrolü ucu **eklemez**; kendi route'unuza koymanız
|
|
170
171
|
gerekir. Varsayılan `devGateBypass` listesi `/api/healthcheck` yolunu içerdiği
|
|
171
|
-
için bu adı kullanmak en az sürprizli seçenektir:
|
|
172
|
-
|
|
172
|
+
için bu adı kullanmak en az sürprizli seçenektir: dev gate açıkken bile
|
|
173
|
+
erişilebilir kalır.
|
|
173
174
|
|
|
174
175
|
```js
|
|
175
176
|
// routes/00-health.mjs
|
|
@@ -326,7 +327,7 @@ istek başına iş neredeyse sıfıra iner.
|
|
|
326
327
|
- [ ] `hooks.prewarmPaths()` en önemli sayfaları başa koyuyor
|
|
327
328
|
- [ ] `headers()` içinde CSP ve güvenlik başlıkları tanımlı
|
|
328
329
|
- [ ] Sağlık kontrolü ucu var ve `devGateBypass` listesinde
|
|
329
|
-
- [ ] Staging'de `DEV_TOKEN` ayarlı, prod'da
|
|
330
|
+
- [ ] Staging'de `DEV_GATE=1` ve `DEV_TOKEN` ayarlı, prod'da gate **kapalı**
|
|
330
331
|
- [ ] Ters proxy `Accept-Encoding`i iletiyor ve kendi sıkıştırmasını yapmıyor
|
|
331
332
|
- [ ] `clientEnv` listesinde gizli anahtar yok
|
|
332
333
|
|
package/docs/11-tasima.md
CHANGED
|
@@ -7,6 +7,29 @@ alt kümesine benzetildi — `next.config` sözdizimi, Metadata API, `notFound()
|
|
|
7
7
|
`revalidate`, `cache()` gibi kavramlar tanıdık gelecek. Farkların *nedenleri*
|
|
8
8
|
[02-mimari.md](./02-mimari.md)'de.
|
|
9
9
|
|
|
10
|
+
## `jskelet migrate` (codemod)
|
|
11
|
+
|
|
12
|
+
App Router ağacına karşı codemod'u çalıştırın. Babel (`@babel/parser`,
|
|
13
|
+
`@babel/types`) JSkelet ile birlikte gelir — ek kurulum yok.
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx jskelet migrate scan ../my-next-app
|
|
17
|
+
npx jskelet migrate apply ../my-next-app --out . --write
|
|
18
|
+
npx jskelet migrate config ../my-next-app --write
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
| Komut | Ne yapar |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| `migrate` / `migrate scan` | Sayfa, layout, `"use client"` modülleri ve engelleri (iç içe layout, Server Actions, Suspense) listeler. |
|
|
24
|
+
| `migrate apply` | **Otomatik çeviri:** `page.*` → feature controller + `.jsk`; presentational bileşenler → `views/components/*.js`; client → island `mount()` iskeleti. Varsayılan dry-run; yazmak için `--write`. Üzerine yazmaz (çakışmada `.migrate` soneki). |
|
|
25
|
+
| `migrate config` | `next.config`'ten `jskelet.config.mjs` taslağı (`headers` / `redirects` / `rewrites`, `images.widths`, `NEXT_PUBLIC_*` → `clientEnv`). |
|
|
26
|
+
|
|
27
|
+
Bayraklar: `--out <dir>`, `--only pages,components,islands`, `--json`, `--strict` (partial/skipped → exit 1).
|
|
28
|
+
|
|
29
|
+
**Otomatik çevrilenler:** `className`, `{{ }}` / `{{{ }}}`, `{#if}` / `{#each}`, `next/image` → `<Image />`, `next/link` → `<Link />`, `dangerouslySetInnerHTML`, `revalidate`, basit controller prelude.
|
|
30
|
+
|
|
31
|
+
**Çevrilmeyenler (raporlanır):** React hooks, Server Actions, iç içe layout düzleştirme, Streaming/Suspense, client routing. Dosya başına `ok` / `partial` / `skipped`.
|
|
32
|
+
|
|
10
33
|
## Karşılık tablosu
|
|
11
34
|
|
|
12
35
|
### Yapılandırma
|
|
@@ -30,8 +53,8 @@ alt kümesine benzetildi — `next.config` sözdizimi, Metadata API, `notFound()
|
|
|
30
53
|
| `app/page.js` (dosya bazlı routing) | `routes/*.mjs` içinde `app.get(...)` | Sıra açık yazılır ([03](./03-routing.md)) |
|
|
31
54
|
| `app/[slug]/page.js` | `app.get("/:slug", route(...))` | Express desen sözdizimi |
|
|
32
55
|
| `params`, `searchParams` | `ctx.params`, `ctx.query` | Controller'ın tek argümanı |
|
|
33
|
-
| `layout.js` | `views/layout.
|
|
34
|
-
| Sunucu bileşeni (RSC) | Controller +
|
|
56
|
+
| `layout.js` | `views/layout.jsk` + `hooks.layoutContext()` | Tek layout; iç içe layout yok |
|
|
57
|
+
| Sunucu bileşeni (RSC) | Controller + `.jsk` şablonu + `views/components/**` | Fonksiyon HTML string döndürür |
|
|
35
58
|
| İstemci bileşeni (`"use client"`) | Island (`data-island` + `mount`) | Sayfanın tamamı hidre edilmez ([05](./05-islands.md)) |
|
|
36
59
|
| `notFound()` | `notFound()` | Aynı ad, aynı kontrol akışı |
|
|
37
60
|
| `redirect()` | `redirect()` (307) | Kalıcı için `permanentRedirect()` (308) |
|
|
@@ -87,10 +110,13 @@ Bunları taşıma planında baştan hesaba katın:
|
|
|
87
110
|
|
|
88
111
|
- **React'in kendisi.** Bileşenler HTML string döndüren fonksiyonlara dönüşür.
|
|
89
112
|
JSX yok, hook yok, sanal DOM yok.
|
|
90
|
-
- **TypeScript.**
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
113
|
+
- **TypeScript.** Framework kaynağı düz JS + JSDoc'tur ve tüketiciler için
|
|
114
|
+
`.d.ts` yayınlar. Client entry/island'lar `.ts` / `.mts` olabilir (esbuild tip
|
|
115
|
+
siler; manifest anahtarı `*.js` kalır). Sunucu route, hook ve
|
|
116
|
+
`jskelet.config.mjs` Node ESM JavaScript kalır — orada editör denetimi için
|
|
117
|
+
`jsconfig.json` içinde `checkJs: true` kullanın.
|
|
118
|
+
- **İç içe layout'lar.** Tek bir layout var; ortak bölümleri `{#include}` ya da
|
|
119
|
+
bileşen fonksiyonlarıyla paylaşırsınız (legacy EJS’te `include`).
|
|
94
120
|
- **Streaming / Suspense / kısmi prerender.** Yanıt tek parça üretilir.
|
|
95
121
|
- **İstemci tarafı yönlendirme.** Gezinme gerçek sayfa yüklemesidir. Sunucu HTML'i
|
|
96
122
|
önbellekten geldiği için pratikte çok hızlıdır, ama SPA geçişleri yoktur.
|
|
@@ -169,12 +195,12 @@ export default function register(app, { route, notFound }) {
|
|
|
169
195
|
}
|
|
170
196
|
```
|
|
171
197
|
|
|
172
|
-
```
|
|
173
|
-
|
|
198
|
+
```jsk
|
|
199
|
+
{# views/pages/article.jsk #}
|
|
174
200
|
<article class="wrapper">
|
|
175
|
-
<h1 class="text-3xl font-bold"
|
|
176
|
-
|
|
177
|
-
<div
|
|
201
|
+
<h1 class="text-3xl font-bold">{{ article.title }}</h1>
|
|
202
|
+
<Image :src="article.cover" :alt="article.title" priority :width="1200" :height="630" />
|
|
203
|
+
<div>{{{ article.body }}}</div>
|
|
178
204
|
</article>
|
|
179
205
|
```
|
|
180
206
|
|
|
@@ -188,12 +214,16 @@ de aynı yazıyı isterse tek upstream isteği yapılmasını sağlar
|
|
|
188
214
|
|
|
189
215
|
Yeni bir dizinde `npx jskelet init` çalıştırın ve `jskelet dev`in açıldığını
|
|
190
216
|
görün. Mevcut Next projesini olduğu gibi bırakın; taşıma paralel yürüsün.
|
|
217
|
+
İsterseniz önce `jskelet migrate scan <next-root>` ile sayfa ve engel listesine bakın.
|
|
191
218
|
|
|
192
219
|
`jsconfig.json` içindeki `paths` alias'larınızı taşıyın — `@/` gibi önekler hem
|
|
193
220
|
sunucuda hem bundle'da aynı şekilde çalışır ([02-mimari.md](./02-mimari.md)).
|
|
194
221
|
|
|
195
222
|
### 2. `next.config.mjs`'i çevir (1-2 saat)
|
|
196
223
|
|
|
224
|
+
`jskelet migrate config <next-root> --write` çoğunu taslaklar; ardından gözden
|
|
225
|
+
geçirin:
|
|
226
|
+
|
|
197
227
|
`headers()`, `redirects()` ve `rewrites()` bölümleri neredeyse birebir kopyalanır.
|
|
198
228
|
Desen sözdizimini kontrol edin: JSkelet `:slug`, `:path*`, `/a-:b` ve
|
|
199
229
|
`/:path*.svg` biçimlerini destekler; daha karmaşık `path-to-regexp` ifadeleri
|
|
@@ -214,9 +244,9 @@ değildir; olduğu gibi kopyalanır. İki değişiklik yapın:
|
|
|
214
244
|
|
|
215
245
|
### 4. Layout'u kur (yarım gün)
|
|
216
246
|
|
|
217
|
-
`app/layout.jsx`'i `views/layout.
|
|
218
|
-
layout'unu (`
|
|
219
|
-
üzerine yazmak en hızlı yol.
|
|
247
|
+
`app/layout.jsx`'i `views/layout.jsk`'e çevirin (veya `migrate apply` taslağını
|
|
248
|
+
kullanın). Framework'ün varsayılan layout'unu (`jskelet/layout` → `.jsk`)
|
|
249
|
+
kopyalayıp üzerine yazmak en hızlı yol.
|
|
220
250
|
|
|
221
251
|
`layout.jsx` içinde veri çekiyorsanız (navigasyon, site ayarları) bunu
|
|
222
252
|
`hooks.layoutContext()` içine taşıyın: gövde render'ıyla paralel çalışır ve
|
|
@@ -227,6 +257,9 @@ Global metadata varsayılanlarını (`titleTemplate`, `siteUrl`, `description`)
|
|
|
227
257
|
|
|
228
258
|
### 5. Bileşenleri çevir (en uzun adım)
|
|
229
259
|
|
|
260
|
+
`jskelet migrate apply --only components --write` hooks'suz presentational
|
|
261
|
+
bileşenleri çevirir. Gerisini elle bitirin:
|
|
262
|
+
|
|
230
263
|
Her React bileşeni bir fonksiyona dönüşür:
|
|
231
264
|
|
|
232
265
|
```jsx
|
|
@@ -260,8 +293,9 @@ Bileşenleri küçük ve saf tutun; veri çekmeyi controller'da bırakın.
|
|
|
260
293
|
|
|
261
294
|
### 6. Sayfaları taşı (sayfa başına saatler)
|
|
262
295
|
|
|
263
|
-
|
|
264
|
-
|
|
296
|
+
`jskelet migrate apply --only pages --write` her `page.*` dosyasını feature
|
|
297
|
+
controller + `.jsk` şablonuna böler. `partial` / `skipped` satırlarını gözden
|
|
298
|
+
geçirip TODO'ları bitirin. Sırayı düşünerek dosyalayın:
|
|
265
299
|
|
|
266
300
|
```
|
|
267
301
|
routes/
|
|
@@ -337,7 +371,7 @@ redirect kurallarının doğruluğunu ölçmek için işe yarar.
|
|
|
337
371
|
## Taşıma sırasında sık yapılan hatalar
|
|
338
372
|
|
|
339
373
|
- **`esc()` unutmak.** JSX'ten gelen alışkanlıkla `${value}` yazmak XSS demektir.
|
|
340
|
-
|
|
374
|
+
`.jsk`'de `{{ }}` (kaçışlı) / `{{{ }}}` (ham); bileşenlerde `esc()` kendiniz.
|
|
341
375
|
- **`@source` eklemeden yeni bir dizin açmak.** Sınıflar sessizce düşer.
|
|
342
376
|
- **Yakalayıcı route'u yanlış sıraya koymak.** `/:slug` her zaman en sonda.
|
|
343
377
|
- **Sayfanın tamamını island yapmak.** Kazanç sunucu HTML'inin tam olmasından
|
|
@@ -143,7 +143,9 @@ export default {
|
|
|
143
143
|
sharedCookieRoots: [".investvio.com", ".localhost"],
|
|
144
144
|
},
|
|
145
145
|
auth: {
|
|
146
|
-
crossSubdomainHandoff:
|
|
146
|
+
crossSubdomainHandoff: {
|
|
147
|
+
allowedCookieNames: ["sid"], // zorunlu allowlist
|
|
148
|
+
},
|
|
147
149
|
},
|
|
148
150
|
};
|
|
149
151
|
```
|
|
@@ -207,15 +209,19 @@ okunur. Yazımdan sonra **read-back** yapılır; tarayıcı Domain'i reddettiyse
|
|
|
207
209
|
|
|
208
210
|
`auth.crossSubdomainHandoff` açıkken:
|
|
209
211
|
|
|
210
|
-
1. `POST /_jskelet/auth/handoff` `{ name, value, next }` → `{ url }` (`?handoff=` ekli)
|
|
212
|
+
1. `POST /_jskelet/auth/handoff` `{ name, value, next }` → `{ url }` (`?handoff=` ekli).
|
|
213
|
+
Mint, CSRF middleware'inden **sonra** mount edilir; `name`
|
|
214
|
+
`allowedCookieNames` içinde ve RFC 6265 token olmalı.
|
|
211
215
|
2. Hedef host'ta GET middleware bileti tek kullanımlık tüketir, cookie yazar
|
|
212
216
|
(önce shared Domain, olmazsa host-only), `handoff` query'siz 303
|
|
213
217
|
|
|
214
218
|
`next` yalnızca aynı `sharedCookieRoots` altındaki host'lara izinli. Bilet
|
|
215
|
-
~60 sn, süreç belleğinde
|
|
219
|
+
~60 sn, süreç belleğinde; bekleyen bilet ve IP başına mint sınırı vardır.
|
|
220
|
+
JWT URL'ye konmaz.
|
|
216
221
|
|
|
217
222
|
`window.name` köprüsü cookie'siz yedek: kaynakta `handoffViaWindowName`,
|
|
218
|
-
hedeefte `consumeWindowNameHandoff`.
|
|
223
|
+
hedeefte `consumeWindowNameHandoff`. Cross-origin tab'da `window.name`
|
|
224
|
+
okunabilir kalır — mümkünse sunucu handoff tercih edin.
|
|
219
225
|
|
|
220
226
|
## CSRF
|
|
221
227
|
|
package/docs/README.md
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
# JSkelet belgeleri
|
|
2
2
|
|
|
3
3
|
JSkelet, SEO ve hız odaklı siteler için "framework'süz hissettiren" bir
|
|
4
|
-
framework: Express 5 +
|
|
5
|
-
island'larla ekler, CSS'i
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
framework: Express 5 + build-time `.jsk` ile sunucuda tam HTML üretir (EJS
|
|
5
|
+
opsiyonel legacy peer), etkileşimi vanilla JS island'larla ekler, CSS'i
|
|
6
|
+
Tailwind v4 ile tek bir stylesheet'e derler ve ISR yerine süreç belleğinde
|
|
7
|
+
yaşayan, stale-while-revalidate'li bir HTML TTL cache kullanır. React yok;
|
|
8
|
+
framework kaynağı düz JavaScript + JSDoc'tur. Uygulama tarafında client
|
|
9
|
+
island/entry'ler TypeScript yazılabilir ve paket `.d.ts` yayınlar.
|
|
8
10
|
|
|
9
11
|
Bu dizin framework'ün tam referansıdır. Sıralı okumak için baştan başlayın;
|
|
10
12
|
belirli bir konuyu arıyorsanız doğrudan ilgili başlığa gidin.
|
|
@@ -19,7 +21,7 @@ eşlenik tutuluyor; birini değiştiriyorsan diğerini de değiştir.
|
|
|
19
21
|
| [01-baslangic.md](./01-baslangic.md) | Kurulum, `jskelet init`, ilk route, ilk island, dizin yapısı, CLI komutları |
|
|
20
22
|
| [02-mimari.md](./02-mimari.md) | Mimari kararlar ve gerekçeleri: island modeli, tam sunucu HTML'i, cache stratejisi, middleware sırası |
|
|
21
23
|
| [03-routing.md](./03-routing.md) | Route modülü sözleşmesi, yükleme sırası, controller sözleşmesi, `ctx`, `notFound`/`redirect`, config redirects/rewrites |
|
|
22
|
-
| [04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md) |
|
|
24
|
+
| [04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md) | `.jsk` layout/sayfalar, otomatik bileşen kaydı, `html`/`tags`, metadata → `<head>`, hook'lar; EJS legacy |
|
|
23
25
|
| [05-islands.md](./05-islands.md) | `data-island` sözleşmesi, hidrasyon stratejileri, `client/entries/*`, `createStore`, DOM yardımcıları, `startSafeImages` |
|
|
24
26
|
| [06-cache.md](./06-cache.md) | `withHtmlCache`, `revalidate`, stale-while-revalidate, cache anahtarı, `X-JSkelet-Cache`, istek içi cache, degraded render, prewarm |
|
|
25
27
|
| [07-yapilandirma.md](./07-yapilandirma.md) | `jskelet.config.mjs` tam referansı, `source` desen sözdizimi, ortam değişkenleri tablosu |
|
|
@@ -45,7 +47,7 @@ eşlenik tutuluyor; birini değiştiriyorsan diğerini de değiştir.
|
|
|
45
47
|
|
|
46
48
|
## Çalışan örnekler
|
|
47
49
|
|
|
48
|
-
|
|
50
|
+
Üçü de çalışır durumda; belgelerdeki örneklerin çoğu buralardan alınmıştır.
|
|
49
51
|
|
|
50
52
|
**`examples/minimal/`** — iki route, bir bileşen, bir island, minimal config.
|
|
51
53
|
Framework'ün en küçük çalışan hâli.
|
|
@@ -66,32 +68,7 @@ npm --prefix examples/blog install
|
|
|
66
68
|
npm --prefix examples/blog run dev
|
|
67
69
|
```
|
|
68
70
|
|
|
69
|
-
**`examples/
|
|
70
|
-
tablosu, canlı gecikme ölçümü, SSS, belgeler dizini, sürüm notları ve indirme
|
|
71
|
-
sayfası. Sayfadaki bayt sayıları `lib/payload.js` içinde sitenin **kendi** build
|
|
72
|
-
çıktısından, sürüm künyesi ise `lib/release.js` içinde kurulu paketin
|
|
73
|
-
`package.json`'ından okunur; gecikme sayıları `latency` island'ında tarayıcıda
|
|
74
|
-
ölçülür. Uzun TTL (bir saat) ve tüm sayfaları ısıtan prewarm ile, cache'in en
|
|
75
|
-
verimli çalıştığı profili gösterir.
|
|
76
|
-
|
|
77
|
-
Site aynı zamanda **bu belgeleri** servis ediyor: `/docs/<bölüm>` adresleri
|
|
78
|
-
`node_modules/jskelet/docs/` altındaki markdown dosyalarını okuyup sol gezinme,
|
|
79
|
-
"bu sayfada" listesi ve sıralı geçişle basıyor. Çevirici `lib/markdown.js`
|
|
80
|
-
içinde küçük bir modül — bağımlılık yok — ve kaynak paketin kendisi olduğu için
|
|
81
|
-
site kurulu sürümden hiç ayrışmıyor.
|
|
82
|
-
|
|
83
|
-
Site aynı zamanda **iki dilli**: varsayılan İngilizce kökte, Türkçe `/tr`
|
|
84
|
-
altında ve route adları iki dilde de aynı. Framework'te i18n yok; dil
|
|
85
|
-
çözümlemesi `lib/i18n.js` içinde uygulamanın kendi sözleşmesi olarak duruyor ve
|
|
86
|
-
`hooks.layoutContext` ile bir sözlüğe bağlanıyor. Çok dilli bir siteyi bu
|
|
87
|
-
yüzeyle nasıl kurabileceğinizi görmek için bakılacak yer burası.
|
|
88
|
-
|
|
89
|
-
```bash
|
|
90
|
-
npm --prefix examples/marketing install
|
|
91
|
-
npm --prefix examples/marketing run dev
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
**`examples/dashboard/`** — diğer üçünün tersi eksen: kişiye özel sayfalar.
|
|
71
|
+
**`examples/dashboard/`** — diğer ikisinin tersi eksen: kişiye özel sayfalar.
|
|
95
72
|
İmzalı cookie ile giriş, `private: true` korumalı panel, sayfalı tablo
|
|
96
73
|
fragment'i, CSRF'li mutasyon formu ve temizlik fonksiyonu döndüren bir island.
|
|
97
74
|
Public bir tanıtım sayfası da var, böylece aynı uygulamada önbelleklenen ve
|
|
@@ -102,5 +79,5 @@ npm --prefix examples/dashboard install
|
|
|
102
79
|
npm --prefix examples/dashboard run dev
|
|
103
80
|
```
|
|
104
81
|
|
|
105
|
-
Her
|
|
82
|
+
Her üç örnekte `node smoke.mjs` sunucu ayaktayken uçların beklendiği gibi
|
|
106
83
|
yanıt verdiğini doğrular.
|
|
@@ -255,11 +255,12 @@ ESM resolve hooks (`--import`) at process start.
|
|
|
255
255
|
|
|
256
256
|
| Command | What it does |
|
|
257
257
|
| --- | --- |
|
|
258
|
-
| `jskelet dev` | Build watch + server, in a single terminal. Live reload, CSS hot-swap, dev overlay. `NODE_ENV=development`. |
|
|
258
|
+
| `jskelet dev` | Build watch + server, in a single terminal. Live reload, CSS hot-swap, dev overlay. `NODE_ENV=development`. Refuses to start if the port is busy; `--murder` kills the listener and binds. |
|
|
259
259
|
| `jskelet build` | One-shot prod build: fonts → icon sprite → CSS → client JS → images → manifest → precompress. `production` if `NODE_ENV` is not given. |
|
|
260
|
-
| `jskelet start` | Prod server. If there is no build output it produces it first. `production` if `NODE_ENV` is not given. |
|
|
260
|
+
| `jskelet start` | Prod server. If there is no build output it produces it first. `production` if `NODE_ENV` is not given. Same port behaviour as `dev` (`--murder`). |
|
|
261
261
|
| `jskelet init` | Installs a feature-first `.jsk` skeleton into the current directory; leaves existing files alone. |
|
|
262
262
|
| `jskelet generate` | Scaffolds a `feature` / `page` / `island`. |
|
|
263
|
+
| `jskelet migrate` | Next.js App Router → JSkelet codemod (`scan` / `apply` / `config`). See [11-migration.md](./11-migration.md). |
|
|
263
264
|
|
|
264
265
|
An unknown command, or a call with no arguments, prints the usage text.
|
|
265
266
|
|
|
@@ -287,7 +288,7 @@ only these specifiers:
|
|
|
287
288
|
| `jskelet/tags` | `link`, `image`, `icon`, `preloadImage`, `toKebab` |
|
|
288
289
|
| `jskelet/log` | Console output helpers (`banner`, `event`, `task`, `size`, `ms`, …) |
|
|
289
290
|
| `jskelet/register` | Alias + extension hooks via `node --import jskelet/register` |
|
|
290
|
-
| `jskelet/layout` | The path to the framework's default `layout.
|
|
291
|
+
| `jskelet/layout` | The path to the framework's default `layout.jsk` file |
|
|
291
292
|
|
|
292
293
|
## What's next
|
|
293
294
|
|
|
@@ -36,9 +36,10 @@ Request
|
|
|
36
36
|
├─ rewrites(beforeFiles) config → proxy or a change to req.url
|
|
37
37
|
├─ compression brotli/gzip negotiation (quality 5)
|
|
38
38
|
├─ headers static cache + config headers()
|
|
39
|
-
├─ devGate if
|
|
39
|
+
├─ devGate if the gate is on, 404 without a token
|
|
40
40
|
├─ redirects config redirects(), first match wins
|
|
41
41
|
├─ trailingSlash 308 when config trailingSlash is true
|
|
42
|
+
├─ robots.txt appends framework Disallow rules to the user's body
|
|
42
43
|
├─ staticPrecompressed .br/.gz copies produced at build (quality 11)
|
|
43
44
|
├─ express.static files under public/
|
|
44
45
|
├─ (dev) devtools only when NODE_ENV=development
|
|
@@ -51,7 +52,7 @@ Request
|
|
|
51
52
|
│ └─ withHtmlCache TTL + stale-while-revalidate
|
|
52
53
|
│ └─ withUpstreamTracking
|
|
53
54
|
│ └─ withRequestCache
|
|
54
|
-
│ └─ controller → renderPage → EJS
|
|
55
|
+
│ └─ controller → renderPage → .jsk (or legacy EJS)
|
|
55
56
|
├─ 404 → hooks.notFound()
|
|
56
57
|
└─ error handling redirect/notFound + 500 fallback
|
|
57
58
|
```
|
|
@@ -71,6 +72,11 @@ position has a reason, and moving things around leads to silent breakage.
|
|
|
71
72
|
leak even its redirect rules to the outside. `trailingSlash` sits after config
|
|
72
73
|
redirects so explicit rules see the requested path first; the canonical slash
|
|
73
74
|
form is enforced as a second step.
|
|
75
|
+
- **`robots.txt` before static, and inside compression.** The body the
|
|
76
|
+
application wrote is left intact; the framework appends `Disallow` rules
|
|
77
|
+
for its own endpoints. A response that already has `Content-Encoding` is
|
|
78
|
+
not rewritten — the block is added to plain text, and compression stays
|
|
79
|
+
outside.
|
|
74
80
|
- **`staticPrecompressed` before `express.static`.** If there are `.br`/`.gz`
|
|
75
81
|
copies produced at build time, those are served (brotli quality 11);
|
|
76
82
|
otherwise the request falls through to the `static` below it and the
|
|
@@ -270,10 +276,10 @@ hard-to-diagnose problems like "why is there no stylesheet".
|
|
|
270
276
|
|
|
271
277
|
## Why this dependency list
|
|
272
278
|
|
|
273
|
-
There are
|
|
274
|
-
`
|
|
275
|
-
Phosphor icons) is an **optional
|
|
276
|
-
corresponding build step is skipped.
|
|
279
|
+
There are three runtime dependencies: `express`, `esbuild`, `tailwind-merge`.
|
|
280
|
+
`ejs` is an optional peer only for legacy `.ejs` templates. Everything else
|
|
281
|
+
(Tailwind, PostCSS, lightningcss, sharp, the Phosphor icons) is an **optional
|
|
282
|
+
peer dependency**, and if it is absent the corresponding build step is skipped.
|
|
277
283
|
|
|
278
284
|
Two decisions deserve a separate explanation:
|
|
279
285
|
|
package/docs/en/03-routing.md
CHANGED
|
@@ -158,7 +158,9 @@ stored), but the flag is the right place. Details in
|
|
|
158
158
|
## `fragment()` — a partial without the layout
|
|
159
159
|
|
|
160
160
|
For endpoints that refresh a region. No layout is printed, the response is sent
|
|
161
|
-
with `private, no-store` and no ETag, and it never touches the HTML cache.
|
|
161
|
+
with `private, no-store` and no ETag, and it never touches the HTML cache. The
|
|
162
|
+
`/_fragment/` prefix is appended to `robots.txt`, so partial endpoints are not
|
|
163
|
+
indexed ([04](./04-rendering.md#robotstxt)).
|
|
162
164
|
|
|
163
165
|
```js
|
|
164
166
|
app.get(
|
|
@@ -213,7 +215,7 @@ fields:
|
|
|
213
215
|
|
|
214
216
|
| Field | Type | Default | Meaning |
|
|
215
217
|
| --- | --- | --- | --- |
|
|
216
|
-
| `view` | `string` | — | The template path under `views/`, without the extension: `"pages/home"` → `views/pages/home.ejs
|
|
218
|
+
| `view` | `string` | — | The template path under `views/`, without the extension: `"pages/home"` → `views/pages/home.jsk` (else legacy `.ejs`). |
|
|
217
219
|
| `data` | `object` | `{}` | Data passed to the template as locals. |
|
|
218
220
|
| `metadata` | `object` | `{}` | Turned into `<head>` tags; it overrides the output of `hooks.metadata()`. Schema: [04-rendering.md](./04-rendering.md). |
|
|
219
221
|
| `status` | `number` | `200` | The HTTP status code. Only 200 is written to the cache. |
|
|
@@ -332,13 +334,18 @@ handler kicks in, logs the error and returns the framework's own error page with
|
|
|
332
334
|
`Cache-Control: no-store`. The status code is read from the error's `statusCode`
|
|
333
335
|
(or `status`) field; if it is not in the 400–599 range, 500 is used.
|
|
334
336
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
to `en`).
|
|
337
|
+
**Development** (`NODE_ENV=development`, i.e. `jskelet dev`): for 5xx responses
|
|
338
|
+
the built-in 500 page and `hooks.error()` are skipped; a diagnostic page with
|
|
339
|
+
the message, stack trace, and any `cause` chain is returned instead. 4xx (404
|
|
340
|
+
and friends) still use the usual status page in development.
|
|
340
341
|
|
|
341
|
-
|
|
342
|
+
**Production**: the framework's page is deliberately plain — status code, a
|
|
343
|
+
one-line heading and a one-line description. It carries no brand name, no
|
|
344
|
+
navigation and no error detail; the innards of the server are not opened up to
|
|
345
|
+
the visitor. The language comes from `brand.lang` (`tr` and `en` are built in,
|
|
346
|
+
others fall back to `en`).
|
|
347
|
+
|
|
348
|
+
To provide your own page, `hooks.error()` (production / 4xx only):
|
|
342
349
|
|
|
343
350
|
```js
|
|
344
351
|
// jskelet.config.mjs
|
package/docs/en/04-rendering.md
CHANGED
|
@@ -52,7 +52,7 @@ controller data → imported render(data, helpers) → HTML
|
|
|
52
52
|
{/if}
|
|
53
53
|
|
|
54
54
|
{#each items as item, i}
|
|
55
|
-
<li data-i="
|
|
55
|
+
<li :data-i="i">{{ item }}</li>
|
|
56
56
|
{/each}
|
|
57
57
|
|
|
58
58
|
<Link href="/" text="Home" />
|
|
@@ -68,7 +68,7 @@ controller data → imported render(data, helpers) → HTML
|
|
|
68
68
|
| Loop | `{#each list as item}` or `as item, i` |
|
|
69
69
|
| Include | `{#include "partials/header"}` (compiled `.jsk`) |
|
|
70
70
|
| Component | PascalCase tag; `:prop="expr"`, `prop="literal"`, boolean `disabled` |
|
|
71
|
-
| Built-ins | `Link`, `Image`, `Icon`, `CsrfField`, `PreloadImage` |
|
|
71
|
+
| Built-ins | `Link`, `Image`, `Icon`, `CsrfField`, `PreloadImage`, `Stylesheets`, `BodyScripts`, `JsonLd` |
|
|
72
72
|
|
|
73
73
|
The expression language is intentionally small (access, compare, ternary,
|
|
74
74
|
`.length`). No assignments, object literals, or arbitrary calls — keep logic in
|
|
@@ -104,12 +104,15 @@ See the extension README for details.
|
|
|
104
104
|
|
|
105
105
|
If a compiled `.jsk` exists for a view id it wins; otherwise `.ejs` is rendered
|
|
106
106
|
with EJS. Existing apps keep working unchanged. `jskelet init` scaffolds `.jsk`.
|
|
107
|
+
Legacy `.ejs` needs the optional `ejs` peer installed in the application
|
|
108
|
+
(`npm install ejs`); without it only `.jsk` templates run.
|
|
107
109
|
|
|
108
110
|
## The EJS engine (legacy)
|
|
109
111
|
|
|
110
|
-
EJS remains supported
|
|
111
|
-
|
|
112
|
-
|
|
112
|
+
EJS remains supported as an **optional peer dependency** for legacy templates.
|
|
113
|
+
The engine is set up once on the first render; the component scan touches the
|
|
114
|
+
file system, so it cannot be done on every request and cannot be computed
|
|
115
|
+
before the config is loaded.
|
|
113
116
|
|
|
114
117
|
Settings:
|
|
115
118
|
|
|
@@ -130,66 +133,54 @@ normal flow because the dev server restarts the process.
|
|
|
130
133
|
|
|
131
134
|
1. `jskelet.config.mjs` → if `layout` is given, it is used. The path is
|
|
132
135
|
resolved relative to the **parent directory of the views directory**: if
|
|
133
|
-
`views` is the default, `layout: "views/custom.
|
|
136
|
+
`views` is the default, `layout: "views/custom.jsk"` → `<root>/views/custom.jsk`.
|
|
134
137
|
2. If it is not given and `views/layout.jsk` exists (compiled), that is used.
|
|
135
|
-
3. Else if `views/layout.ejs` exists, that is used.
|
|
138
|
+
3. Else if `views/layout.ejs` exists (legacy), that is used.
|
|
136
139
|
4. If that does not exist either, the framework's own minimal layout is used
|
|
137
|
-
(`node_modules/jskelet/src/templates/layout.
|
|
140
|
+
(`node_modules/jskelet/src/templates/layout.jsk`, also reachable through the
|
|
138
141
|
`jskelet/layout` specifier).
|
|
139
142
|
|
|
140
143
|
These fallbacks exist so that a new project can work with a single route. The
|
|
141
144
|
most practical way to move to your own layout is to copy that file to
|
|
142
|
-
`views/layout.
|
|
145
|
+
`views/layout.jsk`.
|
|
143
146
|
|
|
144
147
|
### The framework's default layout
|
|
145
148
|
|
|
146
|
-
```
|
|
149
|
+
```html
|
|
147
150
|
<!DOCTYPE html>
|
|
148
|
-
<html lang="
|
|
151
|
+
<html :lang="lang">
|
|
149
152
|
<head>
|
|
150
153
|
<meta charset="utf-8">
|
|
151
154
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
<% styles.forEach(function (sheet) { %>
|
|
157
|
-
<% if (hasAsset(sheet)) { %>
|
|
158
|
-
<link rel="stylesheet" href="<%= asset(sheet) %>" data-jskelet-css="<%= sheet %>">
|
|
159
|
-
<% } %>
|
|
160
|
-
<% }); %>
|
|
161
|
-
<%- headMeta %>
|
|
162
|
-
<% structuredData.forEach(function (item) { %>
|
|
163
|
-
<script type="application/ld+json"><%- jsonScript(item) %></script>
|
|
164
|
-
<% }); %>
|
|
155
|
+
{{{ extraHead }}}
|
|
156
|
+
<Stylesheets :styles="styles" />
|
|
157
|
+
{{{ headMeta }}}
|
|
158
|
+
<JsonLd :items="structuredData" />
|
|
165
159
|
</head>
|
|
166
|
-
<body class="
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
<script type="module" src="<%= asset('main.js') %>"></script>
|
|
170
|
-
<% } %>
|
|
171
|
-
<% entries.forEach(function (entry) { %>
|
|
172
|
-
<script type="module" src="<%= asset(entry) %>"></script>
|
|
173
|
-
<% }); %>
|
|
174
|
-
<% if (devtools) { %>
|
|
175
|
-
<script type="module" src="<%= devBasePath %>/overlay.js"></script>
|
|
176
|
-
<% } %>
|
|
160
|
+
<body :class="bodyClass">
|
|
161
|
+
{{{ body }}}
|
|
162
|
+
<BodyScripts :entries="entries" :devtools="devtools" :devBasePath="devBasePath" />
|
|
177
163
|
</body>
|
|
178
164
|
</html>
|
|
179
165
|
```
|
|
180
166
|
|
|
167
|
+
The `.jsk` expression language has no function calls, so asset loops live in the
|
|
168
|
+
built-in `<Stylesheets />`, `<BodyScripts />` and `<JsonLd />` tags instead of
|
|
169
|
+
inline `hasAsset` / `asset` / `forEach` in the layout.
|
|
170
|
+
|
|
181
171
|
Points to watch:
|
|
182
172
|
|
|
183
173
|
- **`extraHead` comes first.** Delaying resource hints (`preconnect`, LCP
|
|
184
174
|
`preload`) writes straight into LCP.
|
|
185
|
-
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
the tag is
|
|
189
|
-
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
175
|
+
- **`<Stylesheets />` emits global `app.css` (render-blocking)** plus controller
|
|
176
|
+
`styles: [...]`, with the reasoning in
|
|
177
|
+
[02-architecture.md](./02-architecture.md). If the build has not run,
|
|
178
|
+
`hasAsset` is false inside the tag and nothing is emitted.
|
|
179
|
+
- **`<BodyScripts />` emits `main.js`, page `entries`, and the
|
|
180
|
+
development-only overlay.** The overlay script exists only when
|
|
181
|
+
`NODE_ENV=development`; it is absent from production output.
|
|
182
|
+
- **`<JsonLd />` turns `structuredData` into safe
|
|
183
|
+
`application/ld+json` scripts.**
|
|
193
184
|
|
|
194
185
|
### Layout locals
|
|
195
186
|
|
|
@@ -219,30 +210,27 @@ of bug where every page thinks it is the home page and renders the logo as an
|
|
|
219
210
|
## Page templates
|
|
220
211
|
|
|
221
212
|
The `view` field gives the path under `views/` without an extension:
|
|
222
|
-
`"pages/home"` → `views/pages/home.ejs
|
|
223
|
-
the contents of the `data` field plus `metadata` —
|
|
224
|
-
The page template still has access to all helpers
|
|
213
|
+
`"pages/home"` → `views/pages/home.jsk` (else legacy `home.ejs`). The locals
|
|
214
|
+
passed to the template are the contents of the `data` field plus `metadata` —
|
|
215
|
+
**not** the layout locals. The page template still has access to all helpers
|
|
216
|
+
and components.
|
|
225
217
|
|
|
226
|
-
```
|
|
227
|
-
|
|
218
|
+
```html
|
|
219
|
+
{# views/pages/home.jsk #}
|
|
228
220
|
<section class="wrapper">
|
|
229
|
-
<h1 class="text-3xl font-bold"
|
|
221
|
+
<h1 class="text-3xl font-bold">{{ heading }}</h1>
|
|
230
222
|
|
|
231
|
-
|
|
232
|
-
|
|
223
|
+
{# `list` is defined in views/components/list.js; no import needed. #}
|
|
224
|
+
<List :items="items" />
|
|
233
225
|
|
|
234
226
|
<div class="mt-8" data-island="counter" data-island-props='{"start":5}'></div>
|
|
235
227
|
</section>
|
|
236
228
|
```
|
|
237
229
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
be safe (component calls, `headMeta`, `body`).
|
|
243
|
-
|
|
244
|
-
Because `async: true` is on, `await` can also be used inside a template, but
|
|
245
|
-
keeping data fetching in the controller makes diagnosis easier.
|
|
230
|
+
In `.jsk`, `{{ }}` escapes and `{{{ }}}` emits raw HTML (trusted strings only).
|
|
231
|
+
Legacy EJS keeps `<%= %>` / `<%- %>` with the same meaning; because `async: true`
|
|
232
|
+
is on there, `await` can also be used inside an `.ejs` template, but keeping
|
|
233
|
+
data fetching in the controller makes diagnosis easier.
|
|
246
234
|
|
|
247
235
|
## Components: `views/components/**`
|
|
248
236
|
|
|
@@ -522,6 +510,30 @@ The `renderHeadMeta(metadata)` function is exported; it can be used when you
|
|
|
522
510
|
need to produce the same tags outside the layout (for example in a fragment or
|
|
523
511
|
an email).
|
|
524
512
|
|
|
513
|
+
## robots.txt
|
|
514
|
+
|
|
515
|
+
The application writes `robots.txt`: `public/robots.txt` or a plain route.
|
|
516
|
+
The framework does not change that body; it appends a JSkelet note and
|
|
517
|
+
`Disallow` rules **under** a successful text response. If there is no file
|
|
518
|
+
and no route, the framework does not invent a `robots.txt`.
|
|
519
|
+
|
|
520
|
+
Paths added:
|
|
521
|
+
|
|
522
|
+
- `/_jskelet/` — admin panel, remote image proxy, auth handoff
|
|
523
|
+
- `/__jskelet/` — development tools
|
|
524
|
+
- `/_fragment/` — partial responses without a layout
|
|
525
|
+
|
|
526
|
+
An endpoint moved off those prefixes is added too, but only when it is
|
|
527
|
+
actually mounted: `admin.basePath`, `images.remote.path`,
|
|
528
|
+
`auth.crossSubdomainHandoff.path`. `brand.devBasePath` is written only in
|
|
529
|
+
development; in production that path may be the application's own page.
|
|
530
|
+
|
|
531
|
+
The note starts with the configured brand name (`brand.name`, default
|
|
532
|
+
`JSkelet`). The trailing group repeats `User-agent: *` together with every
|
|
533
|
+
other agent already named in the file. Google does not merge a
|
|
534
|
+
crawler-specific group with `*`; it does merge a second group for the same
|
|
535
|
+
agent. If the note is already in the file, it is not appended again.
|
|
536
|
+
|
|
525
537
|
## Dynamic OG images
|
|
526
538
|
|
|
527
539
|
Counterpart to Next.js `ImageResponse` / `opengraph-image.tsx`. There is no JSX:
|