jskelet 0.4.0 → 0.4.2
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 +24 -1
- package/README.md +114 -85
- package/bin/jskelet.mjs +1 -1
- package/docs/01-baslangic.md +54 -49
- package/docs/04-render-ve-sablonlar.md +36 -5
- package/docs/06-cache.md +62 -0
- package/docs/en/01-getting-started.md +55 -50
- package/docs/en/04-rendering.md +37 -5
- package/docs/en/06-caching.md +133 -71
- package/package.json +1 -1
- package/src/compile/index.js +2 -0
- package/src/compile/resolve.js +64 -15
- package/src/compile/scan-exports.js +51 -0
- package/src/init.mjs +42 -35
- package/src/views/components/loader.js +8 -5
package/CHANGELOG.md
CHANGED
|
@@ -27,6 +27,17 @@ one is listed under a **Breaking** heading.
|
|
|
27
27
|
|
|
28
28
|
### Added
|
|
29
29
|
|
|
30
|
+
- VS Code / Cursor extension skeleton under `extensions/vscode-jsk`: `.jsk`
|
|
31
|
+
language id, TextMate highlighting (`{{ }}` / `{#if}` / `{#each}` /
|
|
32
|
+
components), language config, and snippets. Install from that folder or
|
|
33
|
+
launch **JSK: Extension** from the repo root. Bound attrs on HTML tags
|
|
34
|
+
(`:src="… + '/path'"`) highlight nested single-quoted strings.
|
|
35
|
+
- Compile-time known components are discovered from **named exports** in
|
|
36
|
+
`views/components/**/*.js` (plus `.jsk` component files), not from the file
|
|
37
|
+
basename — so `<SectionHead />` resolves when `sectionHead` lives in
|
|
38
|
+
`ui.js` without a stub re-export. Docs cover the `.jsk` template-vs-component
|
|
39
|
+
boundary and a `{ items, error }` loader / `LoadErrorState` pattern so
|
|
40
|
+
upstream failures are not mistaken for empty data.
|
|
30
41
|
- Build-time `.jsk` templates: declarative HTML-like syntax compiled to ESM
|
|
31
42
|
render modules under `.jskelet/templates/` (no request-time parse, `eval`, or
|
|
32
43
|
`new Function`). Coexists with EJS; compiled `.jsk` wins when both exist.
|
|
@@ -36,7 +47,8 @@ one is listed under a **Breaking** heading.
|
|
|
36
47
|
- Feature-first conventions: `paths.features` / `paths.shared`, multi-root
|
|
37
48
|
views and components, `features/<name>/index.js` route registration after
|
|
38
49
|
`routes/`. CLI: `jskelet generate feature|page|island`. `jskelet init`
|
|
39
|
-
scaffolds `.jsk`
|
|
50
|
+
scaffolds a feature-first `.jsk` skeleton (`features/home/` with route,
|
|
51
|
+
page, component and island; global `views/pages/not-found.jsk`).
|
|
40
52
|
- Template compile step in `jskelet build`; icon scan and Tailwind docs cover
|
|
41
53
|
`.jsk` / `features` / `shared`. Bench: `node scripts/bench-templates.mjs`.
|
|
42
54
|
- Top-level `logs` config for persistent sinks: daily NDJSON files
|
|
@@ -127,8 +139,19 @@ one is listed under a **Breaking** heading.
|
|
|
127
139
|
|
|
128
140
|
### Changed
|
|
129
141
|
|
|
142
|
+
- README rewritten for the current surface: build-time `.jsk` as the default
|
|
143
|
+
template story (EJS still supported), feature-first `init` examples, `mount`
|
|
144
|
+
island contract, path-based `invalidateHtmlCache` (replacing the outdated
|
|
145
|
+
“no targeted invalidation” claim), Redis / admin / data-cache callouts, and
|
|
146
|
+
bilingual doc links under `docs/` and `docs/en/`.
|
|
147
|
+
- Duplicate component named exports (or the same PascalCase tag in two files)
|
|
148
|
+
now **fail** at build and at server startup instead of warning and letting
|
|
149
|
+
the second definition win. Overwriting `components/index.js` barrel exports
|
|
150
|
+
remains allowed.
|
|
130
151
|
- `examples/minimal` pages moved to `.jsk`; adds `features/demo` as a
|
|
131
152
|
co-located route + view sample.
|
|
153
|
+
- Marketing compare/FAQ copy no longer claims targeted invalidation is missing;
|
|
154
|
+
it points at `invalidateHtmlCache()` (and Redis pub/sub for multi-instance).
|
|
132
155
|
|
|
133
156
|
- An adaptive per-host rate limit for upstream calls, `cache().upstream`. It sits
|
|
134
157
|
in the `fetch` wrapper rather than in the prewarm pass, because what spends the
|
package/README.md
CHANGED
|
@@ -3,11 +3,12 @@
|
|
|
3
3
|
**A framework that feels like no framework** — for sites where SEO and speed are
|
|
4
4
|
the product.
|
|
5
5
|
|
|
6
|
-
JSkelet renders **complete HTML** on an Express 5 server
|
|
7
|
-
|
|
8
|
-
Tailwind v4 stylesheet**, and instead
|
|
9
|
-
cache** with stale-while-revalidate
|
|
10
|
-
|
|
6
|
+
JSkelet renders **complete HTML** on an Express 5 server from build-time
|
|
7
|
+
**`.jsk` templates** (EJS still works), adds interactivity through vanilla JS
|
|
8
|
+
**islands**, compiles CSS into a **single Tailwind v4 stylesheet**, and instead
|
|
9
|
+
of ISR keeps an in-process **HTML TTL cache** with stale-while-revalidate — plus
|
|
10
|
+
optional Redis sharing and path-based invalidation. No React, no TypeScript —
|
|
11
|
+
plain JavaScript with JSDoc.
|
|
11
12
|
|
|
12
13
|
[](https://www.npmjs.com/package/jskelet)
|
|
13
14
|
[](https://nodejs.org)
|
|
@@ -46,19 +47,17 @@ Requirements:
|
|
|
46
47
|
- **Node.js 22 or newer.**
|
|
47
48
|
- Everything else is an **optional peer dependency**: `postcss`,
|
|
48
49
|
`@tailwindcss/postcss`, `tailwindcss` and `lightningcss` for styles,
|
|
49
|
-
`@phosphor-icons/core` for the icon sprite, `sharp` for image optimization
|
|
50
|
-
|
|
51
|
-
the site keeps working.
|
|
50
|
+
`@phosphor-icons/core` for the icon sprite, `sharp` for image optimization,
|
|
51
|
+
`ioredis` for the shared Redis cache tier. If a package is missing, the
|
|
52
|
+
matching step is skipped with a warning and the site keeps working.
|
|
52
53
|
|
|
53
54
|
## What it looks like
|
|
54
55
|
|
|
55
|
-
A route module receives the app and
|
|
56
|
-
inferred from the file system
|
|
56
|
+
A feature (or route) module receives the app and registers URLs explicitly.
|
|
57
|
+
Nothing is inferred from the file system.
|
|
57
58
|
|
|
58
59
|
```js
|
|
59
|
-
//
|
|
60
|
-
import { getPost, getPosts } from "../lib/posts.js";
|
|
61
|
-
|
|
60
|
+
// features/home/index.js
|
|
62
61
|
export default function register(app, { route, notFound }) {
|
|
63
62
|
app.get("/", route(
|
|
64
63
|
async () => ({
|
|
@@ -66,7 +65,7 @@ export default function register(app, { route, notFound }) {
|
|
|
66
65
|
metadata: { title: "Home", canonical: "/" },
|
|
67
66
|
data: { posts: getPosts() },
|
|
68
67
|
}),
|
|
69
|
-
{ revalidate: 60 },
|
|
68
|
+
{ revalidate: 60 },
|
|
70
69
|
));
|
|
71
70
|
|
|
72
71
|
app.get("/blog/:slug", route(async ({ params }) => {
|
|
@@ -77,28 +76,28 @@ export default function register(app, { route, notFound }) {
|
|
|
77
76
|
}
|
|
78
77
|
```
|
|
79
78
|
|
|
80
|
-
Templates are
|
|
81
|
-
|
|
82
|
-
functions
|
|
79
|
+
Templates are `.jsk`: compiled to ESM at build time (no request-time parse or
|
|
80
|
+
`eval`). Named exports under `views/components/**` become PascalCase tags —
|
|
81
|
+
plain functions that return HTML strings.
|
|
83
82
|
|
|
84
83
|
```html
|
|
85
|
-
<!-- views/pages/home.
|
|
84
|
+
<!-- features/home/views/pages/home.jsk -->
|
|
86
85
|
<section class="wrapper">
|
|
87
86
|
<h1 class="text-3xl font-bold">Latest posts</h1>
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
87
|
+
{#each posts as post}
|
|
88
|
+
<PostCard :post="post" />
|
|
89
|
+
{/each}
|
|
91
90
|
|
|
92
|
-
<!-- downloaded and wired up once visible -->
|
|
93
91
|
<div data-island="newsletter"></div>
|
|
94
92
|
</section>
|
|
95
93
|
```
|
|
96
94
|
|
|
97
|
-
Islands
|
|
95
|
+
Islands export a named `mount(element, props)` and are registered from the
|
|
96
|
+
client entry as dynamic imports.
|
|
98
97
|
|
|
99
98
|
```js
|
|
100
|
-
// client/
|
|
101
|
-
export
|
|
99
|
+
// features/home/client/newsletter.js
|
|
100
|
+
export function mount(el) {
|
|
102
101
|
const form = el.querySelector("form");
|
|
103
102
|
form.addEventListener("submit", async (event) => {
|
|
104
103
|
event.preventDefault();
|
|
@@ -114,20 +113,22 @@ numbered comment at the top of `src/server/create-app.js`; changing that order
|
|
|
114
113
|
causes silent breakage.
|
|
115
114
|
|
|
116
115
|
1. **Config** (`jskelet.config.mjs`) is loaded once and exposed through
|
|
117
|
-
`getConfig()`. `redirects()`, `rewrites()`, `headers()
|
|
118
|
-
the subset of `next.config` syntax people actually use. A
|
|
119
|
-
throwing `headers()` or a failing hook logs a warning and
|
|
120
|
-
defaults — it never takes the site down.
|
|
121
|
-
2. **
|
|
122
|
-
|
|
116
|
+
`getConfig()`. `redirects()`, `rewrites()`, `headers()`, `cache()` and
|
|
117
|
+
`admin()` follow the subset of `next.config` syntax people actually use. A
|
|
118
|
+
broken config, a throwing `headers()` or a failing hook logs a warning and
|
|
119
|
+
falls back to defaults — it never takes the site down.
|
|
120
|
+
2. **Build** turns `.jsk` into modules under `.jskelet/templates/`, bundles
|
|
121
|
+
islands, compiles Tailwind, hashes assets and optionally precompresses them.
|
|
122
|
+
3. **Static assets** are served from `public/` with hashed filenames and
|
|
123
|
+
long-lived cache headers; precompressed `.br` / `.gz` variants are picked
|
|
123
124
|
automatically.
|
|
124
|
-
|
|
125
|
+
4. **`route()`** wraps your controller. It builds a cache key, checks the HTML
|
|
125
126
|
TTL cache, and on a miss renders the page. When a cached entry is stale it is
|
|
126
127
|
returned immediately while revalidation runs in the background.
|
|
127
|
-
|
|
128
|
+
5. **Render** composes metadata, layout context and your view into one HTML
|
|
128
129
|
document. Hooks (`metadata`, `layoutContext`, `notFound`, `prewarmPaths`)
|
|
129
130
|
are where application knowledge lives — the framework itself carries none.
|
|
130
|
-
|
|
131
|
+
6. **Hydration** happens in the browser: the island registry finds
|
|
131
132
|
`data-island` elements and dynamically imports the matching chunk when it
|
|
132
133
|
becomes visible (or eagerly / on idle, if asked).
|
|
133
134
|
|
|
@@ -141,21 +142,35 @@ client.
|
|
|
141
142
|
- **Full HTML from the server.** First paint does not wait for JavaScript, and
|
|
142
143
|
crawlers see the complete document because content is never assembled in the
|
|
143
144
|
browser.
|
|
145
|
+
- **Build-time `.jsk` templates.** Declarative HTML-like syntax compiled to ESM
|
|
146
|
+
before the server starts; EJS remains supported where both exist, `.jsk`
|
|
147
|
+
wins. A VS Code / Cursor extension under `extensions/vscode-jsk` covers
|
|
148
|
+
highlighting and snippets.
|
|
149
|
+
- **Feature-first layout.** `features/<name>/` co-locates routes, views,
|
|
150
|
+
components and islands; `jskelet generate feature|page|island` scaffolds the
|
|
151
|
+
next slice. URLs stay explicit.
|
|
144
152
|
- **Islands.** Interactivity attaches to elements carrying `data-island`.
|
|
145
153
|
Modules are dynamically imported on visibility by default;
|
|
146
154
|
`data-island-eager` and `data-island-idle` pick a different strategy. A small
|
|
147
155
|
store handles sharing state between islands.
|
|
148
156
|
- **HTML TTL cache.** Per-route `revalidate`, stale-while-revalidate on expiry,
|
|
149
|
-
|
|
150
|
-
|
|
157
|
+
query allowlists, dependency tracking from `withDataCache`, and prewarm that
|
|
158
|
+
fills the cache at boot. `invalidateHtmlCache()` stales a path, pattern or
|
|
159
|
+
RegExp without flushing everything.
|
|
160
|
+
- **Optional Redis tier.** With `ioredis`, replicas share HTML/data and
|
|
161
|
+
broadcast invalidation over pub/sub so a webhook reaches every process.
|
|
162
|
+
- **Admin panel.** Opt-in at `/_jskelet/admin` (`admin()` or `JSKELET_ADMIN=1`):
|
|
163
|
+
cache inventory, targeted purge, Cloudflare CDN controls, live logs, routes
|
|
164
|
+
and system meters — password printed once per process start.
|
|
151
165
|
- **Fast navigation.** The `navigation` config section emits Speculation Rules
|
|
152
166
|
to prefetch or prerender links and enables view transitions — without adding
|
|
153
167
|
any client runtime.
|
|
154
168
|
- **Familiar configuration.** `redirects()`, `rewrites()`, `headers()`,
|
|
155
|
-
`cache()`, plus `brand`, `images`, `security
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
169
|
+
`cache()`, `admin()`, plus `brand`, `images`, `security`, `logs`,
|
|
170
|
+
`trailingSlash` and `hooks`.
|
|
171
|
+
- **A real build pipeline.** Fonts, an SVG sprite from the icons you use,
|
|
172
|
+
Tailwind v4 CSS, esbuild bundles with code splitting, webp variants, hashed
|
|
173
|
+
output and brotli/gzip precompression.
|
|
159
174
|
- **Developer experience.** One command, one terminal: watch build plus server,
|
|
160
175
|
CSS hot-swap, automatic restart, and a devtools overlay on Alt+D showing
|
|
161
176
|
requests, errors, upstream calls, a cache dump and Web Vitals.
|
|
@@ -168,8 +183,9 @@ client.
|
|
|
168
183
|
- **No file-system routing.** Paths are written explicitly in route modules.
|
|
169
184
|
- **No streaming or RSC.** A page is flushed as one document; slow sections are
|
|
170
185
|
fetched from separate fragment endpoints.
|
|
171
|
-
- **No
|
|
172
|
-
|
|
186
|
+
- **No Next.js-style cache tags.** Invalidation is by path, pattern or RegExp
|
|
187
|
+
(`invalidateHtmlCache`), plus data-cache dependency tracking — not arbitrary
|
|
188
|
+
tag graphs.
|
|
173
189
|
- **No global state management** beyond the small island store.
|
|
174
190
|
|
|
175
191
|
An app-shaped interface behind a login — a dashboard, an editor, an admin panel
|
|
@@ -177,10 +193,12 @@ An app-shaped interface behind a login — a dashboard, an editor, an admin pane
|
|
|
177
193
|
framework. It is supported rather than recommended: `route(fn, { private: true })`
|
|
178
194
|
keeps per-visitor pages out of the cache, and signed cookies, CSRF, fragment
|
|
179
195
|
endpoints and region swapping cover the rest
|
|
180
|
-
([docs/12-panel-ve-oturum.md](./docs/12-panel-ve-oturum.md)
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
196
|
+
([docs/12-panel-ve-oturum.md](./docs/12-panel-ve-oturum.md) /
|
|
197
|
+
[docs/en/12-dashboards-and-sessions.md](./docs/en/12-dashboards-and-sessions.md)).
|
|
198
|
+
Live data transport is deliberately left to you; pick SSE, WebSocket or polling
|
|
199
|
+
yourself. A feature-by-feature comparison with Next.js is in
|
|
200
|
+
[docs/11-tasima.md](./docs/11-tasima.md) /
|
|
201
|
+
[docs/en/11-migration.md](./docs/en/11-migration.md).
|
|
184
202
|
|
|
185
203
|
## Project layout
|
|
186
204
|
|
|
@@ -189,18 +207,20 @@ the `paths` section of the config:
|
|
|
189
207
|
|
|
190
208
|
```
|
|
191
209
|
my-site/
|
|
192
|
-
├── jskelet.config.mjs
|
|
193
|
-
├──
|
|
194
|
-
|
|
195
|
-
│
|
|
196
|
-
│
|
|
197
|
-
│
|
|
198
|
-
├── client/
|
|
199
|
-
│
|
|
200
|
-
|
|
201
|
-
├──
|
|
202
|
-
├──
|
|
203
|
-
|
|
210
|
+
├── jskelet.config.mjs # config, hooks, headers, redirects
|
|
211
|
+
├── features/ # feature-first slices (optional but default in init)
|
|
212
|
+
│ └── home/
|
|
213
|
+
│ ├── index.js # register(app, api) — URLs stay explicit
|
|
214
|
+
│ ├── views/pages/ # .jsk pages for this feature
|
|
215
|
+
│ ├── views/components/
|
|
216
|
+
│ ├── client/ # islands; register from client/entries
|
|
217
|
+
│ └── server/
|
|
218
|
+
├── routes/ # optional; loaded before features (10-, 20-, …)
|
|
219
|
+
├── views/ # shared / app-wide pages (e.g. 404)
|
|
220
|
+
├── shared/ # cross-feature server/views/client
|
|
221
|
+
├── client/entries/main.js # registers islands, calls start()
|
|
222
|
+
├── styles/globals.css # Tailwind entry with @source directives
|
|
223
|
+
└── public/ # build output plus static files
|
|
204
224
|
```
|
|
205
225
|
|
|
206
226
|
Two things bite newcomers:
|
|
@@ -209,9 +229,8 @@ Two things bite newcomers:
|
|
|
209
229
|
`styles/globals.css`, because automatic detection is turned off with
|
|
210
230
|
`source(none)`. A new directory that uses classes needs an `@source` line, or
|
|
211
231
|
its classes silently vanish from the stylesheet.
|
|
212
|
-
-
|
|
213
|
-
|
|
214
|
-
a `for` loop there.
|
|
232
|
+
- **`.jsk` expression language is intentionally narrow.** Formatting and object
|
|
233
|
+
literals belong in JS components (`views/components/**`), not in the template.
|
|
215
234
|
|
|
216
235
|
## Configuration
|
|
217
236
|
|
|
@@ -220,7 +239,7 @@ Two things bite newcomers:
|
|
|
220
239
|
```js
|
|
221
240
|
export default {
|
|
222
241
|
brand: { name: "My Site", lang: "en" },
|
|
223
|
-
icons: { scan: ["views", "client"] },
|
|
242
|
+
icons: { scan: ["views", "features", "client"] },
|
|
224
243
|
|
|
225
244
|
// Speculation Rules plus @view-transition, with no client runtime.
|
|
226
245
|
navigation: { prefetch: "moderate", prerender: "conservative", viewTransition: true },
|
|
@@ -236,10 +255,17 @@ export default {
|
|
|
236
255
|
async cache() {
|
|
237
256
|
return {
|
|
238
257
|
html: { "/": 3600, "/pricing": 3600 },
|
|
258
|
+
query: { "/search": ["q"] }, // only these params enter the cache key
|
|
239
259
|
prewarm: { enabled: true, max: 50, concurrency: 4 },
|
|
260
|
+
// redis: { enabled: true, url: process.env.REDIS_URL },
|
|
240
261
|
};
|
|
241
262
|
},
|
|
242
263
|
|
|
264
|
+
// Opt-in production panel at /_jskelet/admin (password in the server log).
|
|
265
|
+
async admin() {
|
|
266
|
+
return { enabled: false };
|
|
267
|
+
},
|
|
268
|
+
|
|
243
269
|
hooks: {
|
|
244
270
|
metadata: () => ({ titleTemplate: "%s · My Site", siteUrl: "https://example.com" }),
|
|
245
271
|
layoutContext: ({ pathname }) => ({ pathname, year: new Date().getFullYear() }),
|
|
@@ -249,16 +275,18 @@ export default {
|
|
|
249
275
|
```
|
|
250
276
|
|
|
251
277
|
The complete reference — every field, default and failure mode — is
|
|
252
|
-
[docs/07-yapilandirma.md](./docs/07-yapilandirma.md)
|
|
278
|
+
[docs/07-yapilandirma.md](./docs/07-yapilandirma.md) /
|
|
279
|
+
[docs/en/07-configuration.md](./docs/en/07-configuration.md).
|
|
253
280
|
|
|
254
281
|
## CLI
|
|
255
282
|
|
|
256
283
|
| Command | What it does |
|
|
257
284
|
| --- | --- |
|
|
258
285
|
| `jskelet dev` | Watch build plus server, live reload, devtools overlay |
|
|
259
|
-
| `jskelet build` | Production build: fonts → sprite → CSS → JS → images → manifest → precompress |
|
|
286
|
+
| `jskelet build` | Production build: templates → fonts → sprite → CSS → JS → images → manifest → precompress |
|
|
260
287
|
| `jskelet start` | Production server; builds first if output is missing |
|
|
261
|
-
| `jskelet init` | Scaffolds a
|
|
288
|
+
| `jskelet init` | Scaffolds a feature-first `.jsk` skeleton into the current directory |
|
|
289
|
+
| `jskelet generate` | Scaffolds a `feature` / `page` / `island` |
|
|
262
290
|
|
|
263
291
|
## Public API
|
|
264
292
|
|
|
@@ -266,7 +294,7 @@ Only the specifiers in the `exports` map are supported:
|
|
|
266
294
|
|
|
267
295
|
| Specifier | Contents |
|
|
268
296
|
| --- | --- |
|
|
269
|
-
| `jskelet` | `route`, `fragment`, `createApp`, `startServer`, `notFound`, `redirect`, `seeOther`, `cache`, `asset`, `getConfig`, cookie helpers, HTML cache and prewarm helpers |
|
|
297
|
+
| `jskelet` | `route`, `fragment`, `createApp`, `startServer`, `notFound`, `redirect`, `seeOther`, `cache`, `asset`, `getConfig`, cookie helpers, HTML/data cache and prewarm helpers, Redis/Cloudflare status and purge helpers |
|
|
270
298
|
| `jskelet/client` | `register`, `registerAll`, `hydrate`, `unmount`, `start`, `swap`, `startForms`, `createStore`, DOM helpers |
|
|
271
299
|
| `jskelet/html` | `attrs`, `cn`, `cx`, `esc`, `jsonScript` |
|
|
272
300
|
| `jskelet/tags` | `icon`, `image`, `link`, `preloadImage`, `csrfField` |
|
|
@@ -281,28 +309,29 @@ works: a `Dockerfile` (see `examples/marketing/Dockerfile`), a systemd unit, or
|
|
|
281
309
|
a PaaS. Run `jskelet build` at image build time, put a reverse proxy in front
|
|
282
310
|
for TLS, and expose a health endpoint (the default dev gate bypass list already
|
|
283
311
|
includes `/api/healthcheck`, so a route there is reachable in every mode).
|
|
284
|
-
Details, including cache sizing
|
|
285
|
-
|
|
312
|
+
Details, including cache sizing behind multiple instances and the optional
|
|
313
|
+
Redis tier, are in [docs/10-dagitim.md](./docs/10-dagitim.md) /
|
|
314
|
+
[docs/en/10-deployment.md](./docs/en/10-deployment.md).
|
|
286
315
|
|
|
287
316
|
## Documentation
|
|
288
317
|
|
|
289
|
-
|
|
290
|
-
|
|
318
|
+
Full reference in Turkish under [docs/](./docs/README.md) and in English under
|
|
319
|
+
[docs/en/](./docs/en/README.md). Both editions are kept in sync.
|
|
291
320
|
|
|
292
|
-
| Document | Topic |
|
|
321
|
+
| Document (TR / EN) | Topic |
|
|
293
322
|
| --- | --- |
|
|
294
|
-
| [01-baslangic](./docs/01-baslangic.md) | Installation, first route, first island, directory layout, CLI |
|
|
295
|
-
| [02-mimari](./docs/02-mimari.md) | Decisions and their reasoning, middleware order |
|
|
296
|
-
| [03-routing](./docs/03-routing.md) | Route modules, controller contract, load order |
|
|
297
|
-
| [04-render
|
|
298
|
-
| [05-islands](./docs/05-islands.md) | Island contract, hydration, store, DOM helpers |
|
|
299
|
-
| [06-cache](./docs/06-cache.md) | TTL,
|
|
300
|
-
| [07-yapilandirma](./docs/07-yapilandirma.md) | Complete `jskelet.config.mjs` reference |
|
|
301
|
-
| [08-build](./docs/08-build.md) | Build pipeline, manifest, Tailwind `@source`, sprite |
|
|
302
|
-
| [09-dev-araclari](./docs/09-dev-
|
|
303
|
-
| [10-dagitim](./docs/10-dagitim.md) | Production, Docker, reverse proxy, health checks |
|
|
304
|
-
| [11-tasima](./docs/11-tasima.md) | Migrating from Next.js: mapping table and plan |
|
|
305
|
-
| [12-panel-ve-oturum](./docs/12-
|
|
323
|
+
| [01-baslangic](./docs/01-baslangic.md) / [getting-started](./docs/en/01-getting-started.md) | Installation, first route, first island, directory layout, CLI |
|
|
324
|
+
| [02-mimari](./docs/02-mimari.md) / [architecture](./docs/en/02-architecture.md) | Decisions and their reasoning, middleware order |
|
|
325
|
+
| [03-routing](./docs/03-routing.md) / [routing](./docs/en/03-routing.md) | Route modules, controller contract, load order |
|
|
326
|
+
| [04-render](./docs/04-render-ve-sablonlar.md) / [rendering](./docs/en/04-rendering.md) | `.jsk` / EJS, layout, components, helpers, metadata |
|
|
327
|
+
| [05-islands](./docs/05-islands.md) / [islands](./docs/en/05-islands.md) | Island contract, hydration, store, DOM helpers |
|
|
328
|
+
| [06-cache](./docs/06-cache.md) / [caching](./docs/en/06-caching.md) | TTL, SWR, keys, prewarm, Redis, invalidation, admin |
|
|
329
|
+
| [07-yapilandirma](./docs/07-yapilandirma.md) / [configuration](./docs/en/07-configuration.md) | Complete `jskelet.config.mjs` reference |
|
|
330
|
+
| [08-build](./docs/08-build.md) / [build](./docs/en/08-build.md) | Build pipeline, manifest, Tailwind `@source`, sprite |
|
|
331
|
+
| [09-dev](./docs/09-dev-araclari.md) / [dev-tools](./docs/en/09-dev-tools.md) | Dev workflow, overlay, report page, dev gate |
|
|
332
|
+
| [10-dagitim](./docs/10-dagitim.md) / [deployment](./docs/en/10-deployment.md) | Production, Docker, reverse proxy, health checks |
|
|
333
|
+
| [11-tasima](./docs/11-tasima.md) / [migration](./docs/en/11-migration.md) | Migrating from Next.js: mapping table and plan |
|
|
334
|
+
| [12-panel](./docs/12-panel-ve-oturum.md) / [dashboards](./docs/en/12-dashboards-and-sessions.md) | Per-visitor pages: `private: true`, sessions, CSRF, fragments |
|
|
306
335
|
|
|
307
336
|
If you work with AI agents, [AGENTS.md](./AGENTS.md) summarizes the rules that
|
|
308
337
|
apply to this repository.
|
|
@@ -316,8 +345,8 @@ npm --prefix examples/marketing install && npm --prefix examples/marketing run d
|
|
|
316
345
|
npm --prefix examples/dashboard install && npm --prefix examples/dashboard run dev
|
|
317
346
|
```
|
|
318
347
|
|
|
319
|
-
- **`examples/minimal`** — two routes, one component, one island
|
|
320
|
-
thing that runs.
|
|
348
|
+
- **`examples/minimal`** — two routes, one component, one island, plus a
|
|
349
|
+
co-located `features/demo` slice. The smallest thing that runs.
|
|
321
350
|
- **`examples/blog`** — dynamic routes, tag pages, every config section,
|
|
322
351
|
fragment-loaded tabs, a form, prewarm, RSS and sitemap, four islands. It
|
|
323
352
|
intentionally touches every surface of the framework.
|
package/bin/jskelet.mjs
CHANGED
package/docs/01-baslangic.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Bu belge JSkelet'i sıfırdan çalıştırmayı anlatır: paket kurulumu, `jskelet init`
|
|
4
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
|
|
5
|
+
yapısının ne anlama geldiği ve CLI komutları. Sonunda tarayıcıda sunucuda
|
|
6
6
|
render edilmiş, önbelleğe alınmış ve island'ı görünürlükte hidre olan bir sayfa
|
|
7
7
|
olacak. Kararların *nedenleri* için [02-mimari.md](./02-mimari.md)'ye, buradaki
|
|
8
8
|
her config alanının tam referansı için
|
|
@@ -56,21 +56,24 @@ tamamlar, atlanan dosyaların sayısını uyarı olarak basar. Amaç, "kurulumu
|
|
|
56
56
|
yaptım ama hiçbir şey çalışmıyor" aşamasını tamamen atlamak — `jskelet dev`
|
|
57
57
|
hemen ardından çalışır.
|
|
58
58
|
|
|
59
|
-
Oluşturulan dosyalar:
|
|
59
|
+
Oluşturulan dosyalar (feature-first + `.jsk`):
|
|
60
60
|
|
|
61
61
|
```
|
|
62
|
-
jskelet.config.mjs
|
|
63
|
-
|
|
64
|
-
views/pages/home.
|
|
65
|
-
views/
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
.
|
|
62
|
+
jskelet.config.mjs config: brand, preconnect, cache(), hooks
|
|
63
|
+
features/home/index.js "/" route'u
|
|
64
|
+
features/home/views/pages/home.jsk ana sayfa şablonu
|
|
65
|
+
features/home/views/components/button.js örnek bileşen (<Button />)
|
|
66
|
+
features/home/client/counter.js örnek island
|
|
67
|
+
features/home/server/.gitkeep
|
|
68
|
+
views/pages/not-found.jsk uygulama geneli 404
|
|
69
|
+
client/entries/main.js island bootstrap'ı
|
|
70
|
+
styles/globals.css Tailwind girişi + @source direktifleri
|
|
71
|
+
jsconfig.json checkJs + "@/*" alias'ı
|
|
72
|
+
.gitignore node_modules/, .jskelet/, public/assets/, .env
|
|
72
73
|
```
|
|
73
74
|
|
|
75
|
+
Büyümek için: `npx jskelet generate feature <name>` (veya `page` / `island`).
|
|
76
|
+
|
|
74
77
|
Sonra:
|
|
75
78
|
|
|
76
79
|
```bash
|
|
@@ -88,44 +91,44 @@ ile ezilebilir. Aşağıdaki değerler varsayılanlardır (`src/config/defaults.
|
|
|
88
91
|
|
|
89
92
|
| Dizin | Varsayılan | İçeriği |
|
|
90
93
|
| --- | --- | --- |
|
|
91
|
-
| `views` | `views` |
|
|
94
|
+
| `views` | `views` | Uygulama geneli layout, sayfalar ve bileşenler |
|
|
95
|
+
| `features` | `features` | Feature dilimleri (`<name>/{server,views,client}`) |
|
|
96
|
+
| `shared` | `shared` | Feature'lar arası paylaşılan server/views/client |
|
|
92
97
|
| `public` | `public` | Statik dosyalar; build çıktısı da buraya yazılır |
|
|
93
98
|
| `client` | `client` | Island runtime kaynakları ve entry'ler |
|
|
94
|
-
| `routes` | `routes` | Route modülleri |
|
|
99
|
+
| `routes` | `routes` | Route modülleri (feature'lardan önce yüklenir) |
|
|
95
100
|
| `styles` | `styles/globals.css` | Tailwind/PostCSS giriş **dosyası** |
|
|
96
|
-
| `generated` | `.jskelet` | Build ara çıktıları: `manifest.json`, `metafile.json`, `images.json` |
|
|
101
|
+
| `generated` | `.jskelet` | Build ara çıktıları: `manifest.json`, `metafile.json`, `images.json`, `templates/` |
|
|
97
102
|
|
|
98
103
|
Bunlara ek olarak framework iki yolu her zaman türetir ve ayrı ayar kabul
|
|
99
104
|
etmez: `public/assets` (hash'li build çıktısı) ve `public/fonts` (self-host
|
|
100
105
|
fontlar).
|
|
101
106
|
|
|
102
|
-
Tipik bir proje:
|
|
107
|
+
Tipik bir proje (`jskelet init` çıktısına yakın):
|
|
103
108
|
|
|
104
109
|
```
|
|
105
110
|
benim-sitem/
|
|
106
111
|
├── jskelet.config.mjs
|
|
107
112
|
├── jsconfig.json
|
|
108
|
-
├──
|
|
109
|
-
│
|
|
110
|
-
│
|
|
113
|
+
├── features/
|
|
114
|
+
│ └── home/
|
|
115
|
+
│ ├── index.js
|
|
116
|
+
│ ├── server/
|
|
117
|
+
│ ├── views/
|
|
118
|
+
│ │ ├── pages/home.jsk
|
|
119
|
+
│ │ └── components/button.js
|
|
120
|
+
│ └── client/counter.js
|
|
111
121
|
├── views/
|
|
112
|
-
│
|
|
113
|
-
│ ├── pages/
|
|
114
|
-
│ │ ├── home.ejs
|
|
115
|
-
│ │ └── not-found.ejs
|
|
116
|
-
│ └── components/
|
|
117
|
-
│ └── card.js
|
|
122
|
+
│ └── pages/not-found.jsk
|
|
118
123
|
├── client/
|
|
119
|
-
│
|
|
120
|
-
│ │ └── main.js
|
|
121
|
-
│ └── islands/
|
|
122
|
-
│ └── counter.js
|
|
124
|
+
│ └── entries/main.js
|
|
123
125
|
├── styles/
|
|
124
126
|
│ └── globals.css
|
|
125
127
|
├── public/
|
|
126
128
|
│ └── (statik dosyalar; build → public/assets)
|
|
127
129
|
└── .jskelet/
|
|
128
|
-
|
|
130
|
+
├── manifest.json
|
|
131
|
+
└── templates/
|
|
129
132
|
```
|
|
130
133
|
|
|
131
134
|
## İlk route
|
|
@@ -135,7 +138,7 @@ kendi yollarını `app.get(...)` ile açıkça yazar. Modül sözleşmesi: defau
|
|
|
135
138
|
export ya da `register` adlı named export, `(app, api)` imzasıyla.
|
|
136
139
|
|
|
137
140
|
```js
|
|
138
|
-
//
|
|
141
|
+
// features/home/index.js
|
|
139
142
|
export default function register(app, { route }) {
|
|
140
143
|
app.get(
|
|
141
144
|
"/",
|
|
@@ -143,7 +146,7 @@ export default function register(app, { route }) {
|
|
|
143
146
|
async () => ({
|
|
144
147
|
view: "pages/home",
|
|
145
148
|
metadata: { title: "Ana sayfa" },
|
|
146
|
-
data: {
|
|
149
|
+
data: { message: "JSkelet çalışıyor" },
|
|
147
150
|
}),
|
|
148
151
|
{ revalidate: 60 },
|
|
149
152
|
),
|
|
@@ -157,25 +160,26 @@ yapmak zorunda kalmaz. `route()` controller'ı sarar: HTML cache'i,
|
|
|
157
160
|
notFound/redirect kontrol akışı, sıkıştırma ve `X-JSkelet-Cache` başlığı ondan
|
|
158
161
|
gelir. Controller'ın tek işi bir sayfa tanımı döndürmektir.
|
|
159
162
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
[03-routing.md](./03-routing.md).
|
|
163
|
+
`routes/` kullanıyorsanız dosya adındaki `10-` öneki yükleme sırasını belirler;
|
|
164
|
+
`/:slug` gibi yakalayıcı route'ları daha yüksek numaralı bir dosyaya koyun.
|
|
165
|
+
Feature `index.js` dosyaları `routes/` tarandıktan sonra alfabetik eklenir.
|
|
166
|
+
Ayrıntı: [03-routing.md](./03-routing.md).
|
|
164
167
|
|
|
165
|
-
Şablon tarafı
|
|
168
|
+
Şablon tarafı `.jsk` (build-time derlenir):
|
|
166
169
|
|
|
167
|
-
```
|
|
168
|
-
|
|
170
|
+
```html
|
|
171
|
+
{# features/home/views/pages/home.jsk #}
|
|
169
172
|
<section class="wrapper">
|
|
170
|
-
<h1
|
|
171
|
-
|
|
172
|
-
<
|
|
173
|
+
<h1>{{ metadata.title }}</h1>
|
|
174
|
+
<p>{{ message }}</p>
|
|
175
|
+
<Button text="Örnek bileşen" />
|
|
176
|
+
<div data-island="counter" data-island-props='{"start":0}'></div>
|
|
173
177
|
</section>
|
|
174
178
|
```
|
|
175
179
|
|
|
176
|
-
`
|
|
177
|
-
|
|
178
|
-
|
|
180
|
+
`Button`, `features/home/views/components/button.js` içindeki `button` named
|
|
181
|
+
export'undan gelir — PascalCase etiket; import gerekmez
|
|
182
|
+
([04-render-ve-sablonlar.md](./04-render-ve-sablonlar.md)).
|
|
179
183
|
|
|
180
184
|
## İlk island
|
|
181
185
|
|
|
@@ -193,7 +197,7 @@ iki parçadan oluşur.
|
|
|
193
197
|
verir.
|
|
194
198
|
|
|
195
199
|
```js
|
|
196
|
-
// client/
|
|
200
|
+
// features/home/client/counter.js
|
|
197
201
|
/**
|
|
198
202
|
* @param {HTMLElement} element
|
|
199
203
|
* @param {{ start?: number }} props
|
|
@@ -225,7 +229,7 @@ runtime'ı başlatır.
|
|
|
225
229
|
import { registerAll, start } from "jskelet/client";
|
|
226
230
|
|
|
227
231
|
registerAll({
|
|
228
|
-
counter: () => import("
|
|
232
|
+
counter: () => import("../../features/home/client/counter.js"),
|
|
229
233
|
});
|
|
230
234
|
|
|
231
235
|
start();
|
|
@@ -239,7 +243,7 @@ haritayı büyütmek ilk yükü büyütmez. Hidrasyon stratejileri
|
|
|
239
243
|
|
|
240
244
|
## CLI komutları
|
|
241
245
|
|
|
242
|
-
`bin/jskelet.mjs`
|
|
246
|
+
`bin/jskelet.mjs` şu alt komutları sunar. Her biri ayrı bir Node sürecinde
|
|
243
247
|
çalışır; sebebi `dev`in iki uzun ömürlü süreci yönetmesi ve sunucunun ESM
|
|
244
248
|
resolve hook'larına (`--import`) süreç başlangıcında ihtiyaç duyması.
|
|
245
249
|
|
|
@@ -248,7 +252,8 @@ resolve hook'larına (`--import`) süreç başlangıcında ihtiyaç duyması.
|
|
|
248
252
|
| `jskelet dev` | Build watch + sunucu, tek terminalde. Canlı yenileme, CSS hot-swap, dev overlay. `NODE_ENV=development`. |
|
|
249
253
|
| `jskelet build` | Tek seferlik prod build: fontlar → ikon sprite → CSS → client JS → görseller → manifest → precompress. `NODE_ENV` verilmemişse `production`. |
|
|
250
254
|
| `jskelet start` | Prod sunucu. Build çıktısı yoksa önce üretir. `NODE_ENV` verilmemişse `production`. |
|
|
251
|
-
| `jskelet init` | Bulunduğun dizine
|
|
255
|
+
| `jskelet init` | Bulunduğun dizine feature-first `.jsk` iskeleti kurar; var olan dosyalara dokunmaz. |
|
|
256
|
+
| `jskelet generate` | `feature` / `page` / `island` iskeleti üretir. |
|
|
252
257
|
|
|
253
258
|
Bilinmeyen bir komut ya da argümansız çağrı kullanım metnini basar.
|
|
254
259
|
|