jskelet 0.6.1 → 0.6.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/AGENTS.md +136 -136
- package/CHANGELOG.md +620 -596
- package/LICENSE +21 -21
- package/bin/jskelet.mjs +130 -130
- package/docs/01-baslangic.md +291 -291
- package/docs/02-mimari.md +310 -309
- package/docs/03-routing.md +515 -515
- package/docs/04-render-ve-sablonlar.md +661 -661
- package/docs/05-islands.md +486 -486
- package/docs/06-cache.md +1443 -1423
- package/docs/07-yapilandirma.md +12 -6
- package/docs/08-build.md +429 -428
- package/docs/09-dev-araclari.md +364 -364
- package/docs/10-dagitim.md +338 -338
- package/docs/12-panel-ve-oturum.md +478 -478
- package/docs/README.md +83 -83
- package/docs/en/01-getting-started.md +298 -298
- package/docs/en/02-architecture.md +329 -328
- package/docs/en/03-routing.md +531 -531
- package/docs/en/04-rendering.md +669 -669
- package/docs/en/05-islands.md +497 -497
- package/docs/en/06-caching.md +1453 -1431
- package/docs/en/07-configuration.md +1219 -1214
- package/docs/en/08-build.md +447 -446
- package/docs/en/09-dev-tools.md +373 -373
- package/docs/en/10-deployment.md +340 -340
- package/docs/en/11-migration.md +398 -398
- package/docs/en/12-dashboards-and-sessions.md +488 -488
- package/docs/en/README.md +87 -87
- package/package.json +137 -137
- package/src/build/ensure-build.mjs +19 -19
- package/src/build/paths.mjs +153 -153
- package/src/build/resolve-peer.mjs +36 -36
- package/src/build/tasks/client.mjs +349 -349
- package/src/build/tasks/css.mjs +235 -235
- package/src/build/tasks/fonts.mjs +146 -146
- package/src/build/tasks/icons.mjs +357 -357
- package/src/build/tasks/images.mjs +244 -244
- package/src/build/tasks/precompress.mjs +78 -78
- package/src/build/tasks/templates.mjs +20 -20
- package/src/client/admin/i18n.js +764 -764
- package/src/client/admin/login.html +74 -74
- package/src/client/admin/panel.css +809 -809
- package/src/client/admin/panel.html +495 -495
- package/src/client/admin/panel.js +1251 -1251
- package/src/client/devtools/report.html +185 -185
- package/src/client/devtools/report.js +745 -745
- package/src/client/devtools/seo.js +628 -628
- package/src/client/dom.js +95 -95
- package/src/client/form.js +192 -192
- package/src/client/index.js +45 -45
- package/src/client/registry.js +305 -305
- package/src/client/safe-image.js +91 -91
- package/src/client/shared-cookie.js +225 -225
- package/src/client/store.js +36 -36
- package/src/client/swap.js +188 -188
- package/src/compile/codegen.js +336 -336
- package/src/compile/compile-all.js +149 -149
- package/src/compile/errors.js +66 -66
- package/src/compile/expr.js +409 -409
- package/src/compile/index.js +17 -17
- package/src/compile/parse.js +541 -541
- package/src/compile/resolve.js +211 -211
- package/src/compile/scan-exports.js +51 -51
- package/src/config/defaults.js +17 -1
- package/src/config/index.js +13 -0
- package/src/config/pattern.js +107 -107
- package/src/generate.mjs +163 -163
- package/src/http/control-flow.js +71 -71
- package/src/http/cookies-entry.js +21 -21
- package/src/http/cookies.js +277 -277
- package/src/http/request-cache.js +46 -46
- package/src/http/request-context.js +165 -165
- package/src/http/shared-cookie.js +178 -178
- package/src/index.js +101 -101
- package/src/init.mjs +230 -230
- package/src/migrate/apply.mjs +262 -262
- package/src/migrate/babel.mjs +79 -79
- package/src/migrate/classify.mjs +155 -155
- package/src/migrate/config.mjs +126 -126
- package/src/migrate/fs-walk.mjs +191 -191
- package/src/migrate/parse.mjs +26 -26
- package/src/migrate/scan.mjs +177 -177
- package/src/migrate/transform/expr-source.mjs +168 -168
- package/src/migrate/transform/island.mjs +67 -67
- package/src/migrate/transform/jsx-to-component.mjs +302 -302
- package/src/migrate/transform/jsx-to-jsk.mjs +330 -330
- package/src/migrate/transform/page-split.mjs +435 -435
- package/src/migrate/write.mjs +81 -81
- package/src/migrate.mjs +171 -171
- package/src/runtime/alias-hooks.mjs +119 -119
- package/src/runtime/register.mjs +4 -4
- package/src/server/admin/actions.js +229 -229
- package/src/server/admin/auth.js +125 -125
- package/src/server/admin/event-log.js +151 -151
- package/src/server/admin/gate.js +209 -209
- package/src/server/admin/inventory.js +188 -188
- package/src/server/admin/mount.js +56 -56
- package/src/server/admin/router.js +216 -216
- package/src/server/admin/snapshot.js +241 -241
- package/src/server/assets.js +147 -147
- package/src/server/auth/handoff.js +309 -309
- package/src/server/cache-blob.js +70 -0
- package/src/server/cache-deps.js +42 -42
- package/src/server/cache-vary.js +113 -113
- package/src/server/cloudflare.js +607 -607
- package/src/server/create-app.js +366 -366
- package/src/server/data-cache.js +553 -462
- package/src/server/dev/report.js +485 -485
- package/src/server/dev/socket.js +170 -170
- package/src/server/dev/version-check.mjs +139 -139
- package/src/server/disk-cache.js +233 -0
- package/src/server/ejs-adapter.js +59 -59
- package/src/server/html-cache.js +1196 -1122
- package/src/server/image-optimizer.js +500 -407
- package/src/server/logs/access-middleware.js +66 -66
- package/src/server/logs/file-sink.js +193 -66
- package/src/server/logs/pipeline.js +165 -158
- package/src/server/logs/s3-put.js +214 -214
- package/src/server/logs/s3-sink.js +112 -112
- package/src/server/metadata.js +102 -102
- package/src/server/middleware/compression.js +205 -205
- package/src/server/middleware/csrf.js +134 -134
- package/src/server/middleware/dev-gate.js +75 -75
- package/src/server/middleware/headers.js +37 -37
- package/src/server/middleware/redirects.js +32 -32
- package/src/server/middleware/robots-txt.js +341 -341
- package/src/server/middleware/static-precompressed.js +121 -100
- package/src/server/middleware/trailing-slash.js +53 -53
- package/src/server/middleware/upstream-proxy.js +141 -141
- package/src/server/og-image.js +356 -356
- package/src/server/port-guard.js +255 -255
- package/src/server/prewarm.js +1082 -1058
- package/src/server/redis.js +588 -569
- package/src/server/render.js +4 -4
- package/src/server/router.js +157 -157
- package/src/server/status-page.js +265 -265
- package/src/server/upstream-limiter.js +376 -376
- package/src/server/upstream-tracking.js +166 -166
- package/src/shared/cookie-domain.js +66 -66
- package/src/start.mjs +22 -22
- package/src/templates/layout.ejs +30 -30
- package/src/templates/layout.jsk +30 -30
- package/src/version.mjs +31 -31
- package/src/views/components/loader.js +101 -101
- package/src/views/helpers/html.js +102 -102
- package/src/views/helpers/tags.js +375 -375
- package/types/config/defaults.d.ts +15 -1
- package/types/config/index.d.ts +8 -0
- package/types/server/cache-blob.d.ts +13 -0
- package/types/server/data-cache.d.ts +9 -0
- package/types/server/disk-cache.d.ts +36 -0
- package/types/server/html-cache.d.ts +26 -3
- package/types/server/logs/file-sink.d.ts +16 -5
- package/types/server/redis.d.ts +2 -1
package/src/compile/resolve.js
CHANGED
|
@@ -1,211 +1,211 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* View kökleri ve `.jsk` dosya keşfi.
|
|
3
|
-
*
|
|
4
|
-
* Sıra: views, sonra shared/views, sonra her feature altındaki views.
|
|
5
|
-
* Aynı view id birden fazla kökte varsa son bulunan kazanır.
|
|
6
|
-
*/
|
|
7
|
-
import fs from "node:fs";
|
|
8
|
-
import path from "node:path";
|
|
9
|
-
import { toPascalCase } from "./codegen.js";
|
|
10
|
-
import { CompileError } from "./errors.js";
|
|
11
|
-
import { scanNamedExports } from "./scan-exports.js";
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Runtime'ın camelCase → PascalCase alias'ı ile aynı kural.
|
|
15
|
-
* @param {string} name
|
|
16
|
-
* @returns {string}
|
|
17
|
-
*/
|
|
18
|
-
export function toComponentTag(name) {
|
|
19
|
-
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* @param {{ root: string, dirs: Record<string, string> }} config
|
|
24
|
-
* @returns {string[]} Mutlak view kökleri (var olanlar).
|
|
25
|
-
*/
|
|
26
|
-
export function getViewRoots(config) {
|
|
27
|
-
/** @type {string[]} */
|
|
28
|
-
const roots = [];
|
|
29
|
-
const { dirs, root } = config;
|
|
30
|
-
|
|
31
|
-
if (dirs.views && fs.existsSync(dirs.views)) {
|
|
32
|
-
roots.push(dirs.views);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const sharedViews = dirs.shared
|
|
36
|
-
? path.join(dirs.shared, "views")
|
|
37
|
-
: path.join(root, "shared", "views");
|
|
38
|
-
if (fs.existsSync(sharedViews)) roots.push(sharedViews);
|
|
39
|
-
|
|
40
|
-
const featuresDir = dirs.features ?? path.join(root, "features");
|
|
41
|
-
if (fs.existsSync(featuresDir)) {
|
|
42
|
-
const features = fs
|
|
43
|
-
.readdirSync(featuresDir, { withFileTypes: true })
|
|
44
|
-
.filter((e) => e.isDirectory())
|
|
45
|
-
.map((e) => e.name)
|
|
46
|
-
.sort((a, b) => a.localeCompare(b));
|
|
47
|
-
|
|
48
|
-
for (const name of features) {
|
|
49
|
-
const views = path.join(featuresDir, name, "views");
|
|
50
|
-
if (fs.existsSync(views)) roots.push(views);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
return roots;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Bileşen dizinleri: her view kökü altındaki `components/`.
|
|
59
|
-
* @param {{ root: string, dirs: Record<string, string> }} config
|
|
60
|
-
* @returns {string[]}
|
|
61
|
-
*/
|
|
62
|
-
export function getComponentDirs(config) {
|
|
63
|
-
return getViewRoots(config)
|
|
64
|
-
.map((root) => path.join(root, "components"))
|
|
65
|
-
.filter((dir) => fs.existsSync(dir));
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* @param {string[]} viewRoots
|
|
70
|
-
* @returns {Map<string, string>} viewId → mutlak `.jsk` yolu
|
|
71
|
-
*/
|
|
72
|
-
export function discoverJskFiles(viewRoots) {
|
|
73
|
-
/** @type {Map<string, string>} */
|
|
74
|
-
const map = new Map();
|
|
75
|
-
|
|
76
|
-
for (const root of viewRoots) {
|
|
77
|
-
collectJsk(root, root, map);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return map;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* @param {string} dir
|
|
85
|
-
* @param {string} root
|
|
86
|
-
* @param {Map<string, string>} out
|
|
87
|
-
*/
|
|
88
|
-
function collectJsk(dir, root, out) {
|
|
89
|
-
if (!fs.existsSync(dir)) return;
|
|
90
|
-
|
|
91
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
92
|
-
const full = path.join(dir, entry.name);
|
|
93
|
-
if (entry.isDirectory()) {
|
|
94
|
-
collectJsk(full, root, out);
|
|
95
|
-
continue;
|
|
96
|
-
}
|
|
97
|
-
if (!entry.name.endsWith(".jsk")) continue;
|
|
98
|
-
const rel = path.relative(root, full).split(path.sep).join("/");
|
|
99
|
-
const viewId = rel.replace(/\.jsk$/i, "");
|
|
100
|
-
out.set(viewId, full);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* `components/foo-bar.jsk` → `FooBar`
|
|
106
|
-
* @param {string} viewId
|
|
107
|
-
* @returns {string | null}
|
|
108
|
-
*/
|
|
109
|
-
export function componentNameFromViewId(viewId) {
|
|
110
|
-
const parts = viewId.split("/");
|
|
111
|
-
if (parts[0] !== "components" || parts.length < 2) return null;
|
|
112
|
-
const base = parts[parts.length - 1];
|
|
113
|
-
return toPascalCase(base);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
/**
|
|
117
|
-
* Bilinen bileşen adları: JS named export'lar + derlenecek `.jsk` bileşenleri
|
|
118
|
-
* + yerleşik etiketler.
|
|
119
|
-
*
|
|
120
|
-
* JS tarafında dosya adı varsayılmaz; kaynak metinden `export` adları okunur.
|
|
121
|
-
* Aynı export (veya aynı PascalCase etiket) iki dosyada varsa derleme hatası.
|
|
122
|
-
*
|
|
123
|
-
* @param {string[]} componentDirs
|
|
124
|
-
* @param {Map<string, string>} jskFiles
|
|
125
|
-
* @returns {Set<string>}
|
|
126
|
-
*/
|
|
127
|
-
export function collectKnownComponents(componentDirs, jskFiles) {
|
|
128
|
-
const known = new Set([
|
|
129
|
-
"Link",
|
|
130
|
-
"Image",
|
|
131
|
-
"Icon",
|
|
132
|
-
"CsrfField",
|
|
133
|
-
"PreloadImage",
|
|
134
|
-
"Stylesheets",
|
|
135
|
-
"BodyScripts",
|
|
136
|
-
"JsonLd",
|
|
137
|
-
]);
|
|
138
|
-
|
|
139
|
-
/** @type {Map<string, string>} export adı → göreli yol */
|
|
140
|
-
const byName = new Map();
|
|
141
|
-
/** @type {Map<string, string>} PascalCase etiket → göreli yol */
|
|
142
|
-
const byTag = new Map();
|
|
143
|
-
|
|
144
|
-
for (const [viewId] of jskFiles) {
|
|
145
|
-
const name = componentNameFromViewId(viewId);
|
|
146
|
-
if (!name) continue;
|
|
147
|
-
known.add(name);
|
|
148
|
-
byTag.set(name, `${viewId}.jsk`);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
for (const dir of componentDirs) {
|
|
152
|
-
collectJsComponentNames(dir, known, byName, byTag);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return known;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* @param {string} name
|
|
160
|
-
* @param {string} origin
|
|
161
|
-
* @param {Map<string, string>} byName
|
|
162
|
-
* @param {Map<string, string>} byTag
|
|
163
|
-
*/
|
|
164
|
-
function registerExportOrigin(name, origin, byName, byTag) {
|
|
165
|
-
const previousName = byName.get(name);
|
|
166
|
-
if (previousName && previousName !== origin) {
|
|
167
|
-
throw new CompileError(
|
|
168
|
-
`Component '${name}' is defined twice: ${previousName} and ${origin}`,
|
|
169
|
-
);
|
|
170
|
-
}
|
|
171
|
-
byName.set(name, origin);
|
|
172
|
-
|
|
173
|
-
const tag = toComponentTag(name);
|
|
174
|
-
const previousTag = byTag.get(tag);
|
|
175
|
-
if (previousTag && previousTag !== origin) {
|
|
176
|
-
throw new CompileError(
|
|
177
|
-
`Component '${tag}' is defined twice: ${previousTag} and ${origin}`,
|
|
178
|
-
);
|
|
179
|
-
}
|
|
180
|
-
byTag.set(tag, origin);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* @param {string} dir
|
|
185
|
-
* @param {Set<string>} out
|
|
186
|
-
* @param {Map<string, string>} byName
|
|
187
|
-
* @param {Map<string, string>} byTag
|
|
188
|
-
*/
|
|
189
|
-
function collectJsComponentNames(dir, out, byName, byTag) {
|
|
190
|
-
if (!fs.existsSync(dir)) return;
|
|
191
|
-
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
192
|
-
const full = path.join(dir, entry.name);
|
|
193
|
-
if (entry.isDirectory()) {
|
|
194
|
-
collectJsComponentNames(full, out, byName, byTag);
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
if (!entry.name.endsWith(".js")) continue;
|
|
198
|
-
if (entry.name === "loader.js" || entry.name === "index.js") continue;
|
|
199
|
-
|
|
200
|
-
// Kimlik mutlak yol: çoklu kökte iki `list.js` aynı göreli ada sahip olabilir.
|
|
201
|
-
const origin = full.split(path.sep).join("/");
|
|
202
|
-
const source = fs.readFileSync(full, "utf8");
|
|
203
|
-
const exports = scanNamedExports(source);
|
|
204
|
-
|
|
205
|
-
for (const name of exports) {
|
|
206
|
-
registerExportOrigin(name, origin, byName, byTag);
|
|
207
|
-
out.add(name);
|
|
208
|
-
out.add(toComponentTag(name));
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* View kökleri ve `.jsk` dosya keşfi.
|
|
3
|
+
*
|
|
4
|
+
* Sıra: views, sonra shared/views, sonra her feature altındaki views.
|
|
5
|
+
* Aynı view id birden fazla kökte varsa son bulunan kazanır.
|
|
6
|
+
*/
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { toPascalCase } from "./codegen.js";
|
|
10
|
+
import { CompileError } from "./errors.js";
|
|
11
|
+
import { scanNamedExports } from "./scan-exports.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Runtime'ın camelCase → PascalCase alias'ı ile aynı kural.
|
|
15
|
+
* @param {string} name
|
|
16
|
+
* @returns {string}
|
|
17
|
+
*/
|
|
18
|
+
export function toComponentTag(name) {
|
|
19
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @param {{ root: string, dirs: Record<string, string> }} config
|
|
24
|
+
* @returns {string[]} Mutlak view kökleri (var olanlar).
|
|
25
|
+
*/
|
|
26
|
+
export function getViewRoots(config) {
|
|
27
|
+
/** @type {string[]} */
|
|
28
|
+
const roots = [];
|
|
29
|
+
const { dirs, root } = config;
|
|
30
|
+
|
|
31
|
+
if (dirs.views && fs.existsSync(dirs.views)) {
|
|
32
|
+
roots.push(dirs.views);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const sharedViews = dirs.shared
|
|
36
|
+
? path.join(dirs.shared, "views")
|
|
37
|
+
: path.join(root, "shared", "views");
|
|
38
|
+
if (fs.existsSync(sharedViews)) roots.push(sharedViews);
|
|
39
|
+
|
|
40
|
+
const featuresDir = dirs.features ?? path.join(root, "features");
|
|
41
|
+
if (fs.existsSync(featuresDir)) {
|
|
42
|
+
const features = fs
|
|
43
|
+
.readdirSync(featuresDir, { withFileTypes: true })
|
|
44
|
+
.filter((e) => e.isDirectory())
|
|
45
|
+
.map((e) => e.name)
|
|
46
|
+
.sort((a, b) => a.localeCompare(b));
|
|
47
|
+
|
|
48
|
+
for (const name of features) {
|
|
49
|
+
const views = path.join(featuresDir, name, "views");
|
|
50
|
+
if (fs.existsSync(views)) roots.push(views);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return roots;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Bileşen dizinleri: her view kökü altındaki `components/`.
|
|
59
|
+
* @param {{ root: string, dirs: Record<string, string> }} config
|
|
60
|
+
* @returns {string[]}
|
|
61
|
+
*/
|
|
62
|
+
export function getComponentDirs(config) {
|
|
63
|
+
return getViewRoots(config)
|
|
64
|
+
.map((root) => path.join(root, "components"))
|
|
65
|
+
.filter((dir) => fs.existsSync(dir));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* @param {string[]} viewRoots
|
|
70
|
+
* @returns {Map<string, string>} viewId → mutlak `.jsk` yolu
|
|
71
|
+
*/
|
|
72
|
+
export function discoverJskFiles(viewRoots) {
|
|
73
|
+
/** @type {Map<string, string>} */
|
|
74
|
+
const map = new Map();
|
|
75
|
+
|
|
76
|
+
for (const root of viewRoots) {
|
|
77
|
+
collectJsk(root, root, map);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return map;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* @param {string} dir
|
|
85
|
+
* @param {string} root
|
|
86
|
+
* @param {Map<string, string>} out
|
|
87
|
+
*/
|
|
88
|
+
function collectJsk(dir, root, out) {
|
|
89
|
+
if (!fs.existsSync(dir)) return;
|
|
90
|
+
|
|
91
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
92
|
+
const full = path.join(dir, entry.name);
|
|
93
|
+
if (entry.isDirectory()) {
|
|
94
|
+
collectJsk(full, root, out);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
if (!entry.name.endsWith(".jsk")) continue;
|
|
98
|
+
const rel = path.relative(root, full).split(path.sep).join("/");
|
|
99
|
+
const viewId = rel.replace(/\.jsk$/i, "");
|
|
100
|
+
out.set(viewId, full);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* `components/foo-bar.jsk` → `FooBar`
|
|
106
|
+
* @param {string} viewId
|
|
107
|
+
* @returns {string | null}
|
|
108
|
+
*/
|
|
109
|
+
export function componentNameFromViewId(viewId) {
|
|
110
|
+
const parts = viewId.split("/");
|
|
111
|
+
if (parts[0] !== "components" || parts.length < 2) return null;
|
|
112
|
+
const base = parts[parts.length - 1];
|
|
113
|
+
return toPascalCase(base);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Bilinen bileşen adları: JS named export'lar + derlenecek `.jsk` bileşenleri
|
|
118
|
+
* + yerleşik etiketler.
|
|
119
|
+
*
|
|
120
|
+
* JS tarafında dosya adı varsayılmaz; kaynak metinden `export` adları okunur.
|
|
121
|
+
* Aynı export (veya aynı PascalCase etiket) iki dosyada varsa derleme hatası.
|
|
122
|
+
*
|
|
123
|
+
* @param {string[]} componentDirs
|
|
124
|
+
* @param {Map<string, string>} jskFiles
|
|
125
|
+
* @returns {Set<string>}
|
|
126
|
+
*/
|
|
127
|
+
export function collectKnownComponents(componentDirs, jskFiles) {
|
|
128
|
+
const known = new Set([
|
|
129
|
+
"Link",
|
|
130
|
+
"Image",
|
|
131
|
+
"Icon",
|
|
132
|
+
"CsrfField",
|
|
133
|
+
"PreloadImage",
|
|
134
|
+
"Stylesheets",
|
|
135
|
+
"BodyScripts",
|
|
136
|
+
"JsonLd",
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
/** @type {Map<string, string>} export adı → göreli yol */
|
|
140
|
+
const byName = new Map();
|
|
141
|
+
/** @type {Map<string, string>} PascalCase etiket → göreli yol */
|
|
142
|
+
const byTag = new Map();
|
|
143
|
+
|
|
144
|
+
for (const [viewId] of jskFiles) {
|
|
145
|
+
const name = componentNameFromViewId(viewId);
|
|
146
|
+
if (!name) continue;
|
|
147
|
+
known.add(name);
|
|
148
|
+
byTag.set(name, `${viewId}.jsk`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
for (const dir of componentDirs) {
|
|
152
|
+
collectJsComponentNames(dir, known, byName, byTag);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return known;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* @param {string} name
|
|
160
|
+
* @param {string} origin
|
|
161
|
+
* @param {Map<string, string>} byName
|
|
162
|
+
* @param {Map<string, string>} byTag
|
|
163
|
+
*/
|
|
164
|
+
function registerExportOrigin(name, origin, byName, byTag) {
|
|
165
|
+
const previousName = byName.get(name);
|
|
166
|
+
if (previousName && previousName !== origin) {
|
|
167
|
+
throw new CompileError(
|
|
168
|
+
`Component '${name}' is defined twice: ${previousName} and ${origin}`,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
byName.set(name, origin);
|
|
172
|
+
|
|
173
|
+
const tag = toComponentTag(name);
|
|
174
|
+
const previousTag = byTag.get(tag);
|
|
175
|
+
if (previousTag && previousTag !== origin) {
|
|
176
|
+
throw new CompileError(
|
|
177
|
+
`Component '${tag}' is defined twice: ${previousTag} and ${origin}`,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
byTag.set(tag, origin);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* @param {string} dir
|
|
185
|
+
* @param {Set<string>} out
|
|
186
|
+
* @param {Map<string, string>} byName
|
|
187
|
+
* @param {Map<string, string>} byTag
|
|
188
|
+
*/
|
|
189
|
+
function collectJsComponentNames(dir, out, byName, byTag) {
|
|
190
|
+
if (!fs.existsSync(dir)) return;
|
|
191
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
192
|
+
const full = path.join(dir, entry.name);
|
|
193
|
+
if (entry.isDirectory()) {
|
|
194
|
+
collectJsComponentNames(full, out, byName, byTag);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
if (!entry.name.endsWith(".js")) continue;
|
|
198
|
+
if (entry.name === "loader.js" || entry.name === "index.js") continue;
|
|
199
|
+
|
|
200
|
+
// Kimlik mutlak yol: çoklu kökte iki `list.js` aynı göreli ada sahip olabilir.
|
|
201
|
+
const origin = full.split(path.sep).join("/");
|
|
202
|
+
const source = fs.readFileSync(full, "utf8");
|
|
203
|
+
const exports = scanNamedExports(source);
|
|
204
|
+
|
|
205
|
+
for (const name of exports) {
|
|
206
|
+
registerExportOrigin(name, origin, byName, byTag);
|
|
207
|
+
out.add(name);
|
|
208
|
+
out.add(toComponentTag(name));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
@@ -1,51 +1,51 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Kaynak metinden named export adlarını çıkarır — modülü çalıştırmadan.
|
|
3
|
-
* Compile-time bilinen bileşen listesi için; `default` yok sayılır.
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Yorumları kaba şekilde siler; string içindeki sahte eşleşmeler nadirdir.
|
|
8
|
-
* @param {string} source
|
|
9
|
-
* @returns {string}
|
|
10
|
-
*/
|
|
11
|
-
function stripComments(source) {
|
|
12
|
-
return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* `export { a, b as C, default as X }` listesinden dışa verilen adları toplar.
|
|
17
|
-
* @param {string} clause
|
|
18
|
-
* @param {Set<string>} out
|
|
19
|
-
*/
|
|
20
|
-
function addExportList(clause, out) {
|
|
21
|
-
for (const part of clause.split(",")) {
|
|
22
|
-
const trimmed = part.trim();
|
|
23
|
-
if (!trimmed) continue;
|
|
24
|
-
const bits = trimmed.split(/\s+as\s+/i);
|
|
25
|
-
const exported = (bits[1] ?? bits[0]).trim();
|
|
26
|
-
if (!exported || exported === "default") continue;
|
|
27
|
-
out.add(exported);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* @param {string} source
|
|
33
|
-
* @returns {string[]}
|
|
34
|
-
*/
|
|
35
|
-
export function scanNamedExports(source) {
|
|
36
|
-
const text = stripComments(source);
|
|
37
|
-
/** @type {Set<string>} */
|
|
38
|
-
const names = new Set();
|
|
39
|
-
|
|
40
|
-
for (const match of text.matchAll(
|
|
41
|
-
/\bexport\s+(?:async\s+)?(?:function\*?|class|const|let|var)\s+([A-Za-z_$][\w$]*)/g,
|
|
42
|
-
)) {
|
|
43
|
-
names.add(match[1]);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
for (const match of text.matchAll(/\bexport\s*\{([^}]+)\}/g)) {
|
|
47
|
-
addExportList(match[1], names);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return [...names];
|
|
51
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* Kaynak metinden named export adlarını çıkarır — modülü çalıştırmadan.
|
|
3
|
+
* Compile-time bilinen bileşen listesi için; `default` yok sayılır.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Yorumları kaba şekilde siler; string içindeki sahte eşleşmeler nadirdir.
|
|
8
|
+
* @param {string} source
|
|
9
|
+
* @returns {string}
|
|
10
|
+
*/
|
|
11
|
+
function stripComments(source) {
|
|
12
|
+
return source.replace(/\/\*[\s\S]*?\*\//g, "").replace(/\/\/.*$/gm, "");
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* `export { a, b as C, default as X }` listesinden dışa verilen adları toplar.
|
|
17
|
+
* @param {string} clause
|
|
18
|
+
* @param {Set<string>} out
|
|
19
|
+
*/
|
|
20
|
+
function addExportList(clause, out) {
|
|
21
|
+
for (const part of clause.split(",")) {
|
|
22
|
+
const trimmed = part.trim();
|
|
23
|
+
if (!trimmed) continue;
|
|
24
|
+
const bits = trimmed.split(/\s+as\s+/i);
|
|
25
|
+
const exported = (bits[1] ?? bits[0]).trim();
|
|
26
|
+
if (!exported || exported === "default") continue;
|
|
27
|
+
out.add(exported);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* @param {string} source
|
|
33
|
+
* @returns {string[]}
|
|
34
|
+
*/
|
|
35
|
+
export function scanNamedExports(source) {
|
|
36
|
+
const text = stripComments(source);
|
|
37
|
+
/** @type {Set<string>} */
|
|
38
|
+
const names = new Set();
|
|
39
|
+
|
|
40
|
+
for (const match of text.matchAll(
|
|
41
|
+
/\bexport\s+(?:async\s+)?(?:function\*?|class|const|let|var)\s+([A-Za-z_$][\w$]*)/g,
|
|
42
|
+
)) {
|
|
43
|
+
names.add(match[1]);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (const match of text.matchAll(/\bexport\s*\{([^}]+)\}/g)) {
|
|
47
|
+
addExportList(match[1], names);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return [...names];
|
|
51
|
+
}
|
package/src/config/defaults.js
CHANGED
|
@@ -169,6 +169,19 @@ export const HTML_CACHE_MAX_ENTRIES_CEILING = 800;
|
|
|
169
169
|
*/
|
|
170
170
|
export const HTML_CACHE_BYTE_BUDGET = 256 * 1024 * 1024;
|
|
171
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Süreç içi veri önbelleğinin JSON bayt tavanı (64 MB). Sayı tavanı
|
|
174
|
+
* şişman gövdeleri tutmaz; config yükseltemez. Tek değer bütçeden büyükse
|
|
175
|
+
* saklanmaz, çağıran sonucu yine alır.
|
|
176
|
+
*/
|
|
177
|
+
export const DATA_CACHE_BYTE_BUDGET = 64 * 1024 * 1024;
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Uzak görsel disk önbelleğinin tavanı (256 MB). `.jskelet/image-cache/`
|
|
181
|
+
* bu boyutu aşınca en eski dosya düşer. Config yükseltemez.
|
|
182
|
+
*/
|
|
183
|
+
export const IMAGE_CACHE_BYTE_BUDGET = 256 * 1024 * 1024;
|
|
184
|
+
|
|
172
185
|
/** `cache().data.maxEntries` için sert tavan. Uzun kuyruk burada durur, HTML'de değil. */
|
|
173
186
|
export const DATA_CACHE_MAX_ENTRIES_CEILING = 20_000;
|
|
174
187
|
|
|
@@ -297,7 +310,10 @@ export const DEFAULT_LOGS = {
|
|
|
297
310
|
enabled: false,
|
|
298
311
|
/** Proje köküne göre relative. */
|
|
299
312
|
dir: "logs",
|
|
300
|
-
/**
|
|
313
|
+
/**
|
|
314
|
+
* Artık kullanılmıyor. Parçalar en fazla 5 dakika durur; alan çözülen
|
|
315
|
+
* config'te durur ki eski okuyucular kırılmasın.
|
|
316
|
+
*/
|
|
301
317
|
rotate: /** @type {"daily"} */ ("daily"),
|
|
302
318
|
},
|
|
303
319
|
s3: {
|
package/src/config/index.js
CHANGED
|
@@ -97,6 +97,10 @@ const CONFIG_FILE = "jskelet.config.mjs";
|
|
|
97
97
|
* basılsın mı (banner/build satırları etkilenmez).
|
|
98
98
|
* @property {LogKind[]} kinds Sink'lere giden kayıt türleri.
|
|
99
99
|
* @property {{ enabled: boolean, dir: string, rotate: "daily" }} file
|
|
100
|
+
* `rotate` durur; dosya parçaları en fazla 5 dakika tutulur.
|
|
101
|
+
* @property {import('../server/logs/file-sink.js').DrainLog | null} drainLog
|
|
102
|
+
* Mühürlenen zstd parçasını uygulamanın seçtiği yere aktarır. Hata
|
|
103
|
+
* siteyi düşürmez.
|
|
100
104
|
* @property {{ enabled: boolean, bucket: string | null, prefix: string,
|
|
101
105
|
* region: string | null, endpoint: string | null, flushIntervalMs: number,
|
|
102
106
|
* maxBatch: number }} s3
|
|
@@ -578,6 +582,14 @@ export function normalizeLogs(raw) {
|
|
|
578
582
|
envText(process.env.JSKELET_S3_REGION) ??
|
|
579
583
|
"auto";
|
|
580
584
|
|
|
585
|
+
/** @type {import('../server/logs/file-sink.js').DrainLog | null} */
|
|
586
|
+
let drainLog = null;
|
|
587
|
+
if (typeof source.drainLog === "function") {
|
|
588
|
+
drainLog = source.drainLog;
|
|
589
|
+
} else if (source.drainLog != null) {
|
|
590
|
+
console.warn("[config] logs.drainLog must be a function, ignoring it");
|
|
591
|
+
}
|
|
592
|
+
|
|
581
593
|
const credentials = readS3CredentialsFromEnv();
|
|
582
594
|
// `JSKELET_LOG_BUCKET` (veya S3 bucket) + credential varsa config'te
|
|
583
595
|
// `enabled: true` unutulmuş olsa bile aç. Açık `enabled: false` ezer.
|
|
@@ -603,6 +615,7 @@ export function normalizeLogs(raw) {
|
|
|
603
615
|
: DEFAULT_LOGS.file.dir,
|
|
604
616
|
rotate: "daily",
|
|
605
617
|
},
|
|
618
|
+
drainLog,
|
|
606
619
|
s3: {
|
|
607
620
|
enabled,
|
|
608
621
|
bucket: bucketPath.bucket,
|