jskelet 0.7.2 → 0.7.3
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/CHANGELOG.md +4 -0
- package/docs/02-mimari.md +11 -3
- package/docs/04-render-ve-sablonlar.md +32 -3
- package/docs/08-build.md +5 -2
- package/docs/en/02-architecture.md +10 -3
- package/docs/en/04-rendering.md +36 -4
- package/docs/en/08-build.md +7 -4
- package/package.json +1 -1
- package/src/build/tasks/css.mjs +2 -1
- package/src/views/helpers/tags.js +38 -9
- package/types/views/helpers/tags.d.ts +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -73,6 +73,10 @@ one is listed under a **Breaking** heading.
|
|
|
73
73
|
|
|
74
74
|
### Added
|
|
75
75
|
|
|
76
|
+
- `stylesheets({ blocking: false })` and `<Stylesheets :blocking="false" />`
|
|
77
|
+
emit each stylesheet with `media="print"` and an `onload` that switches it
|
|
78
|
+
to `all`, plus a `<noscript>` fallback, so the links do not block rendering.
|
|
79
|
+
The default stays render-blocking.
|
|
76
80
|
- Dev tools **Server** tab can clear the HTML cache for the open page, every
|
|
77
81
|
HTML entry, or the data cache. Live stats include HTML/data entry counts;
|
|
78
82
|
new endpoints under `devBasePath`: `POST /cache/clear-html`,
|
package/docs/02-mimari.md
CHANGED
|
@@ -155,9 +155,17 @@ kapsamadığı için sheet gelince sayfa yeniden akıyordu (bir liste sayfasınd
|
|
|
155
155
|
ziyarette zaten `immutable` önbellekten geliyor.
|
|
156
156
|
|
|
157
157
|
Sayfaya özel kurallar için `styles/pages/*.css` + controller `styles: [...]`
|
|
158
|
-
eklenebilir ([08-build.md](./08-build.md)); bunlar da
|
|
159
|
-
ama yalnızca ilgili sayfada. Tailwind utility'leri
|
|
160
|
-
sayfa sheet'inde tam `@import "tailwindcss"` utility
|
|
158
|
+
eklenebilir ([08-build.md](./08-build.md)); bunlar da varsayılan olarak
|
|
159
|
+
render-blocking basılır ama yalnızca ilgili sayfada. Tailwind utility'leri
|
|
160
|
+
global sheet'te kalır — sayfa sheet'inde tam `@import "tailwindcss"` utility
|
|
161
|
+
çıktısını tekrarlar.
|
|
162
|
+
|
|
163
|
+
Erken boyama isteyen bir layout bu varsayılandan çıkabilir:
|
|
164
|
+
`stylesheets({ blocking: false })` veya şablonda
|
|
165
|
+
`<Stylesheets :blocking="false" />`. Link `media="print"` ile iner, `onload`
|
|
166
|
+
onu `all` yapar; JS kapalıyken `<noscript>` içindeki normal link uygulanır.
|
|
167
|
+
Bu, yukarıdaki CLS takasının tersidir: ilk boyama erken gelir, sheet gelince
|
|
168
|
+
düzen kayabilir.
|
|
161
169
|
|
|
162
170
|
Aynı mantık ikonlarda da var: her ikon için ayrı istek yerine, build zamanında
|
|
163
171
|
yalnızca kaynakta kullanılan sembollerden bir SVG sprite üretilir. Tüm Phosphor
|
|
@@ -113,7 +113,7 @@ script ve JSON-LD döngüleri için yerleşikler:
|
|
|
113
113
|
|
|
114
114
|
| Etiket | Props | Çıktı |
|
|
115
115
|
| --- | --- | --- |
|
|
116
|
-
| `Stylesheets` | `styles` | `app.css` + sayfa sheet’leri (`data-jskelet-css`) |
|
|
116
|
+
| `Stylesheets` | `styles`, `blocking` | `app.css` + sayfa sheet’leri (`data-jskelet-css`). `blocking` varsayılan `true` |
|
|
117
117
|
| `BodyScripts` | `entries`, `devtools`, `devBasePath` | `main.js`, entry’ler, isteğe bağlı overlay |
|
|
118
118
|
| `JsonLd` | `items` (`structuredData`) | `application/ld+json` script’leri |
|
|
119
119
|
|
|
@@ -181,9 +181,12 @@ Dikkat edilecek noktalar:
|
|
|
181
181
|
|
|
182
182
|
- **`extraHead` en başta.** Kaynak ipuçlarını (`preconnect`, LCP `preload`)
|
|
183
183
|
geciktirmek doğrudan LCP'ye yazılır.
|
|
184
|
-
- **Global `app.css` render-blocking** ve gerekçesi
|
|
184
|
+
- **Global `app.css` varsayılan olarak render-blocking** ve gerekçesi
|
|
185
185
|
[02-mimari.md](./02-mimari.md)'de. Controller `styles: [...]` ile ek sayfa
|
|
186
|
-
sheet'leri de aynı şekilde basılır.
|
|
186
|
+
sheet'leri de aynı şekilde basılır. `<Stylesheets :blocking="false" />`
|
|
187
|
+
(`stylesheets({ blocking: false })`) linkleri `media="print"` + `onload`
|
|
188
|
+
ile basar; JS kapalıyken `<noscript>` yedeği uygulanır. Boyama beklenmez,
|
|
189
|
+
sheet gelince düzen kayabilir. Build çalışmadıysa `hasAsset` false olur
|
|
187
190
|
ve etiket hiç basılmaz.
|
|
188
191
|
- **`hasAsset` kontrolleri** build eksikken sayfanın 404 veren dosyaları
|
|
189
192
|
istememesini sağlar (`Stylesheets` / `BodyScripts` içinde).
|
|
@@ -432,6 +435,32 @@ icon({ name: "ArrowRight", weight: "bold", size: 20, class: "text-slate-500" });
|
|
|
432
435
|
çalışma anında hesaplanan bir çağrı eksik sembole işaret ederse ekranda
|
|
433
436
|
sessizce boşluk kalır ([08-build.md](./08-build.md)).
|
|
434
437
|
|
|
438
|
+
### `stylesheets(props)`
|
|
439
|
+
|
|
440
|
+
Layout `<head>` içindeki global `app.css` ve controller `styles` dizisi.
|
|
441
|
+
Doğrudan çağırmak yerine yerleşik etiket kullanılır:
|
|
442
|
+
|
|
443
|
+
```jsk
|
|
444
|
+
<Stylesheets :styles="styles" />
|
|
445
|
+
<Stylesheets :styles="styles" :blocking="false" />
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
```js
|
|
449
|
+
stylesheets({ styles: ["home.css"] });
|
|
450
|
+
stylesheets({ styles: ["home.css"], blocking: false });
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
- `styles` manifest anahtarlarıdır (`"home.css"` → `styles/pages/home.css`).
|
|
454
|
+
`hasAsset` false olanlar basılmaz.
|
|
455
|
+
- `blocking` varsayılan `true`: `<link rel="stylesheet" data-jskelet-css="…">`.
|
|
456
|
+
Yalnızca boolean `false` beklemeyi kapatır. Çıktı `media="print"` ve
|
|
457
|
+
`onload="this.media='all'"` olur; eşleşmeyen media boyamayı bekletmez.
|
|
458
|
+
`<noscript>` içindeki ikinci link, JS kapalıyken stillerin print'te
|
|
459
|
+
kalmamasını sağlar. Inline `onload` çalışmayan bir CSP'de (`script-src`
|
|
460
|
+
içinde `'unsafe-inline'` veya `script-src-attr` yok) sheet `print`'te kalır.
|
|
461
|
+
- `data-jskelet-css` dev hot-swap'ın doğru linki seçmesi içindir; her iki
|
|
462
|
+
modda da yazılır.
|
|
463
|
+
|
|
435
464
|
### `preloadImage(props)`
|
|
436
465
|
|
|
437
466
|
```js
|
package/docs/08-build.md
CHANGED
|
@@ -141,8 +141,10 @@ minifikasyon → `writeAsset("app.css", …)`.
|
|
|
141
141
|
şekilde yavaşlatıyor.
|
|
142
142
|
- **lightningcss opsiyoneldir:** yoksa Tailwind'in kendi çıktısı kullanılır,
|
|
143
143
|
yalnızca birkaç kB daha büyük olur.
|
|
144
|
-
- Global çıktı `app.css`'tir ve layout onu her sayfada
|
|
145
|
-
yükler.
|
|
144
|
+
- Global çıktı `app.css`'tir ve layout onu her sayfada varsayılan olarak
|
|
145
|
+
render-blocking yükler. `<Stylesheets :blocking="false" />` bu beklemeyi
|
|
146
|
+
kapatır ([04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md)). Ayrı bir
|
|
147
|
+
"critical CSS" üretilmemesinin ölçüm gerekçesi
|
|
146
148
|
[02-mimari.md](./02-mimari.md)'de.
|
|
147
149
|
|
|
148
150
|
### Sayfa stylesheet'leri (`styles/pages/`)
|
|
@@ -166,6 +168,7 @@ kalmalı — dosyada tam `@import "tailwindcss"` utility çıktısını tekrarla
|
|
|
166
168
|
|
|
167
169
|
Layout `app.css`ten sonra `styles` dizisindeki her sheet için
|
|
168
170
|
`<link data-jskelet-css="…">` basar; `hasAsset` false ise etiket yok.
|
|
171
|
+
`blocking: false` aynı linklere `media="print"` ve `onload` ekler.
|
|
169
172
|
|
|
170
173
|
### `@source` direktifleri zorunludur
|
|
171
174
|
|
|
@@ -160,9 +160,16 @@ both faster and free of CLS; on the second visit it already comes from the
|
|
|
160
160
|
|
|
161
161
|
Page-specific rules can use `styles/pages/*.css` plus controller
|
|
162
162
|
`styles: [...]` ([08-build.md](./08-build.md)); those are also render-blocking
|
|
163
|
-
but only on the pages that ask for them. Keep Tailwind utilities
|
|
164
|
-
sheet — a full `@import "tailwindcss"` in a page sheet
|
|
165
|
-
output.
|
|
163
|
+
by default, but only on the pages that ask for them. Keep Tailwind utilities
|
|
164
|
+
in the global sheet — a full `@import "tailwindcss"` in a page sheet
|
|
165
|
+
duplicates utility output.
|
|
166
|
+
|
|
167
|
+
A layout that wants an earlier first paint can leave this default:
|
|
168
|
+
`stylesheets({ blocking: false })`, or `<Stylesheets :blocking="false" />`
|
|
169
|
+
in the template. The link downloads with `media="print"`, `onload` switches
|
|
170
|
+
it to `all`, and a plain link inside `<noscript>` applies when JS is off.
|
|
171
|
+
That is the reverse of the CLS tradeoff above: first paint arrives sooner,
|
|
172
|
+
and the layout can shift when the sheet arrives.
|
|
166
173
|
|
|
167
174
|
The same logic applies to icons: instead of a separate request per icon, an SVG
|
|
168
175
|
sprite is produced at build time from only the symbols actually used in the
|
package/docs/en/04-rendering.md
CHANGED
|
@@ -180,10 +180,14 @@ Points to watch:
|
|
|
180
180
|
|
|
181
181
|
- **`extraHead` comes first.** Delaying resource hints (`preconnect`, LCP
|
|
182
182
|
`preload`) writes straight into LCP.
|
|
183
|
-
- **`<Stylesheets />` emits global `app.css`
|
|
184
|
-
`styles: [...]`, with the reasoning in
|
|
185
|
-
[02-architecture.md](./02-architecture.md).
|
|
186
|
-
|
|
183
|
+
- **`<Stylesheets />` emits global `app.css` render-blocking by default**,
|
|
184
|
+
plus controller `styles: [...]`, with the reasoning in
|
|
185
|
+
[02-architecture.md](./02-architecture.md).
|
|
186
|
+
`<Stylesheets :blocking="false" />` (`stylesheets({ blocking: false })`)
|
|
187
|
+
emits the links with `media="print"` plus `onload`, and a `<noscript>`
|
|
188
|
+
fallback when JS is off. Painting is not held, and the layout can shift
|
|
189
|
+
when the sheet arrives. If the build has not run, `hasAsset` is false
|
|
190
|
+
inside the tag and nothing is emitted.
|
|
187
191
|
- **`<BodyScripts />` emits `main.js`, page `entries`, and the
|
|
188
192
|
development-only overlay.** The overlay script exists only when
|
|
189
193
|
`NODE_ENV=development`; it is absent from production output.
|
|
@@ -436,6 +440,34 @@ icon({ name: "ArrowRight", weight: "bold", size: 20, class: "text-slate-500" });
|
|
|
436
440
|
points at a missing symbol, the screen is silently left blank
|
|
437
441
|
([08-build.md](./08-build.md)).
|
|
438
442
|
|
|
443
|
+
### `stylesheets(props)`
|
|
444
|
+
|
|
445
|
+
Global `app.css` and the controller `styles` list for the layout `<head>`.
|
|
446
|
+
The built-in tag is the usual call site:
|
|
447
|
+
|
|
448
|
+
```jsk
|
|
449
|
+
<Stylesheets :styles="styles" />
|
|
450
|
+
<Stylesheets :styles="styles" :blocking="false" />
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
```js
|
|
454
|
+
stylesheets({ styles: ["home.css"] });
|
|
455
|
+
stylesheets({ styles: ["home.css"], blocking: false });
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
- `styles` are manifest keys (`"home.css"` → `styles/pages/home.css`). Names
|
|
459
|
+
for which `hasAsset` is false are omitted.
|
|
460
|
+
- `blocking` defaults to `true`:
|
|
461
|
+
`<link rel="stylesheet" data-jskelet-css="…">`. Only the boolean `false`
|
|
462
|
+
turns waiting off. The output is `media="print"` and
|
|
463
|
+
`onload="this.media='all'"`; a non-matching media does not hold painting.
|
|
464
|
+
A second link inside `<noscript>` keeps styles from staying on `print`
|
|
465
|
+
when JS is off. Under a CSP that blocks inline handlers (no
|
|
466
|
+
`'unsafe-inline'` in `script-src`, and no `script-src-attr`) the sheet
|
|
467
|
+
stays on `print`.
|
|
468
|
+
- `data-jskelet-css` is how dev hot-swap picks the right link; it is written
|
|
469
|
+
in both modes.
|
|
470
|
+
|
|
439
471
|
### `preloadImage(props)`
|
|
440
472
|
|
|
441
473
|
```js
|
package/docs/en/08-build.md
CHANGED
|
@@ -145,9 +145,11 @@ The pipeline: PostCSS + `@tailwindcss/postcss` → minification with lightningcs
|
|
|
145
145
|
noticeably.
|
|
146
146
|
- **lightningcss is optional:** without it, Tailwind's own output is used, and it
|
|
147
147
|
is only a few kB bigger.
|
|
148
|
-
- The global output is `app.css` and the layout loads it render-blocking on
|
|
149
|
-
page
|
|
150
|
-
|
|
148
|
+
- The global output is `app.css` and the layout loads it render-blocking on
|
|
149
|
+
every page by default. `<Stylesheets :blocking="false" />` turns that wait
|
|
150
|
+
off ([04-rendering.md](./04-rendering.md)). The measurement-based reasoning
|
|
151
|
+
for not producing a separate "critical CSS" is in
|
|
152
|
+
[02-architecture.md](./02-architecture.md).
|
|
151
153
|
|
|
152
154
|
### Page stylesheets (`styles/pages/`)
|
|
153
155
|
|
|
@@ -170,7 +172,8 @@ Page CSS is for page-specific rules. Keep Tailwind utilities in the global
|
|
|
170
172
|
sheet — a full `@import "tailwindcss"` in a page file duplicates utility output.
|
|
171
173
|
|
|
172
174
|
After `app.css`, the layout emits a `<link data-jskelet-css="…">` for each name
|
|
173
|
-
in `styles`; if `hasAsset` is false the tag is omitted.
|
|
175
|
+
in `styles`; if `hasAsset` is false the tag is omitted. `blocking: false`
|
|
176
|
+
adds `media="print"` and `onload` to those same links.
|
|
174
177
|
|
|
175
178
|
### `@source` directives are mandatory
|
|
176
179
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jskelet",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "A framework that feels like no framework: Express 5 + build-time .jsk SSR (optional EJS peer), vanilla JS islands, Tailwind v4 and an in-process HTML TTL cache.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
package/src/build/tasks/css.mjs
CHANGED
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
* CLS 0.307) ve aynı ~27 KB her HTML yanıtında tekrar ediyordu. Sıkıştırılmış
|
|
11
11
|
* global sheet'i render-blocking bırakmak hem daha hızlı hem CLS'siz; ikinci
|
|
12
12
|
* ziyarette zaten immutable önbellekten geliyor. Sayfa sheet'leri de
|
|
13
|
-
* render-blocking; yalnızca ilgili sayfada ek maliyet.
|
|
13
|
+
* varsayılan olarak render-blocking; yalnızca ilgili sayfada ek maliyet.
|
|
14
|
+
* Layout `stylesheets({ blocking: false })` ile bu beklemeyi kapatabilir.
|
|
14
15
|
*
|
|
15
16
|
* Tailwind utility'leri global sheet'te kalmalı. Sayfa CSS'inde tam
|
|
16
17
|
* `@import "tailwindcss"` utility çıktısını tekrarlar; sayfaya özel kurallar
|
|
@@ -324,28 +324,57 @@ export function toKebab(name) {
|
|
|
324
324
|
* build yokken 404 link'i istememek için. `.jsk` ifade dilinde `asset()` /
|
|
325
325
|
* `hasAsset()` çağrılamadığı için layout bu etiketi kullanır.
|
|
326
326
|
*
|
|
327
|
-
*
|
|
327
|
+
* `blocking` varsayılan `true`: `<link rel="stylesheet">` boyamayı bekletir.
|
|
328
|
+
* Ölçümde bu, sheet geç gelince oluşan CLS'ten daha ucuz. `false` yalnızca
|
|
329
|
+
* açık boolean'dır (`:blocking="false"`); atlamak veya `true` aynı çıktıyı
|
|
330
|
+
* verir.
|
|
331
|
+
*
|
|
332
|
+
* @param {{ styles?: string[], blocking?: boolean }} [props]
|
|
328
333
|
* @returns {string}
|
|
329
334
|
*/
|
|
330
335
|
export function stylesheets(props = {}) {
|
|
331
336
|
const styles = Array.isArray(props.styles) ? props.styles : [];
|
|
337
|
+
const blocking = props.blocking !== false;
|
|
332
338
|
/** @type {string[]} */
|
|
333
339
|
const parts = [];
|
|
334
340
|
|
|
335
|
-
|
|
336
|
-
parts.push(
|
|
337
|
-
`<link rel="stylesheet" href="${esc(asset("app.css"))}" data-jskelet-css="app.css">`,
|
|
338
|
-
);
|
|
339
|
-
}
|
|
341
|
+
pushStylesheet(parts, "app.css", blocking);
|
|
340
342
|
|
|
341
343
|
for (const sheet of styles) {
|
|
342
|
-
if (!sheet
|
|
344
|
+
if (!sheet) continue;
|
|
345
|
+
pushStylesheet(parts, sheet, blocking);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return parts.join("\n ");
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Tek bir manifest anahtarı için stylesheet linki.
|
|
353
|
+
*
|
|
354
|
+
* `blocking: false` iken `media="print"`: eşleşmeyen media tarayıcıda
|
|
355
|
+
* render-blocking sayılmaz, indirme boyamayı bekletmez. `onload` sheet
|
|
356
|
+
* inince `media`'yı `all` yapar. JS kapalıyken `onload` çalışmaz ve link
|
|
357
|
+
* print'te kalır; `<noscript>` içindeki normal link o yolu kapatır.
|
|
358
|
+
*
|
|
359
|
+
* @param {string[]} parts
|
|
360
|
+
* @param {string} name Manifest anahtarı (`app.css`, `home.css`, …)
|
|
361
|
+
* @param {boolean} blocking
|
|
362
|
+
*/
|
|
363
|
+
function pushStylesheet(parts, name, blocking) {
|
|
364
|
+
if (!hasAsset(name)) return;
|
|
365
|
+
|
|
366
|
+
const href = esc(asset(name));
|
|
367
|
+
const id = esc(name);
|
|
368
|
+
if (blocking) {
|
|
343
369
|
parts.push(
|
|
344
|
-
`<link rel="stylesheet" href="${
|
|
370
|
+
`<link rel="stylesheet" href="${href}" data-jskelet-css="${id}">`,
|
|
345
371
|
);
|
|
372
|
+
return;
|
|
346
373
|
}
|
|
347
374
|
|
|
348
|
-
|
|
375
|
+
parts.push(
|
|
376
|
+
`<link rel="stylesheet" href="${href}" media="print" onload="this.media='all'" data-jskelet-css="${id}">\n <noscript><link rel="stylesheet" href="${href}" data-jskelet-css="${id}"></noscript>`,
|
|
377
|
+
);
|
|
349
378
|
}
|
|
350
379
|
|
|
351
380
|
/**
|
|
@@ -102,11 +102,17 @@ export declare function toKebab(name: string): string;
|
|
|
102
102
|
* build yokken 404 link'i istememek için. `.jsk` ifade dilinde `asset()` /
|
|
103
103
|
* `hasAsset()` çağrılamadığı için layout bu etiketi kullanır.
|
|
104
104
|
*
|
|
105
|
-
*
|
|
105
|
+
* `blocking` varsayılan `true`: `<link rel="stylesheet">` boyamayı bekletir.
|
|
106
|
+
* Ölçümde bu, sheet geç gelince oluşan CLS'ten daha ucuz. `false` yalnızca
|
|
107
|
+
* açık boolean'dır (`:blocking="false"`); atlamak veya `true` aynı çıktıyı
|
|
108
|
+
* verir.
|
|
109
|
+
*
|
|
110
|
+
* @param {{ styles?: string[], blocking?: boolean }} [props]
|
|
106
111
|
* @returns {string}
|
|
107
112
|
*/
|
|
108
113
|
export declare function stylesheets(props?: {
|
|
109
114
|
styles?: string[];
|
|
115
|
+
blocking?: boolean;
|
|
110
116
|
}): string;
|
|
111
117
|
/**
|
|
112
118
|
* Layout gövde sonu script'leri: `main.js`, controller `entries`, isteğe
|