jskelet 0.6.4 → 0.6.6
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 +10 -0
- package/docs/04-render-ve-sablonlar.md +33 -0
- package/docs/07-yapilandirma.md +41 -0
- package/docs/08-build.md +22 -2
- package/docs/10-dagitim.md +4 -1
- package/docs/en/04-rendering.md +33 -0
- package/docs/en/07-configuration.md +41 -0
- package/docs/en/08-build.md +22 -2
- package/docs/en/10-deployment.md +4 -0
- package/package.json +1 -1
- package/src/build/chunk-groups.mjs +331 -0
- package/src/build/tasks/client.mjs +53 -6
- package/src/config/defaults.js +25 -0
- package/src/config/index.js +125 -0
- package/src/server/og-image.js +621 -25
- package/src/server/og-raster.mjs +21 -0
- package/types/config/defaults.d.ts +24 -0
- package/types/config/index.d.ts +19 -0
- package/types/server/og-image.d.ts +46 -3
|
@@ -15,6 +15,7 @@ import fs from "node:fs";
|
|
|
15
15
|
import path from "node:path";
|
|
16
16
|
import process from "node:process";
|
|
17
17
|
import * as esbuild from "esbuild";
|
|
18
|
+
import { chunkGroupsPlugin } from "../chunk-groups.mjs";
|
|
18
19
|
import { paths, patchManifest, pruneAssets } from "../paths.mjs";
|
|
19
20
|
import * as log from "../../log.mjs";
|
|
20
21
|
|
|
@@ -187,13 +188,34 @@ export async function buildClient(config, { watch = false } = {}) {
|
|
|
187
188
|
.split(path.sep)
|
|
188
189
|
.join("/");
|
|
189
190
|
|
|
191
|
+
const clientBuild = config.build?.client ?? {
|
|
192
|
+
splitting: true,
|
|
193
|
+
groups: {},
|
|
194
|
+
chunkGroup: null,
|
|
195
|
+
};
|
|
196
|
+
const splitting = clientBuild.splitting !== false;
|
|
197
|
+
const groups = splitting ? (clientBuild.groups ?? {}) : {};
|
|
198
|
+
const chunkGroup = splitting ? (clientBuild.chunkGroup ?? null) : null;
|
|
199
|
+
|
|
200
|
+
const groupPlugin = splitting
|
|
201
|
+
? chunkGroupsPlugin({
|
|
202
|
+
root: config.root,
|
|
203
|
+
groups,
|
|
204
|
+
chunkGroup,
|
|
205
|
+
})
|
|
206
|
+
: null;
|
|
207
|
+
|
|
208
|
+
/** @type {esbuild.Plugin[]} */
|
|
209
|
+
const plugins = [aliasPlugin(config.root)];
|
|
210
|
+
if (groupPlugin) plugins.push(groupPlugin);
|
|
211
|
+
|
|
190
212
|
/** @type {esbuild.BuildOptions} */
|
|
191
213
|
const options = {
|
|
192
214
|
entryPoints,
|
|
193
215
|
outdir: outDir,
|
|
194
216
|
absWorkingDir: config.root,
|
|
195
217
|
bundle: true,
|
|
196
|
-
splitting
|
|
218
|
+
splitting,
|
|
197
219
|
format: "esm",
|
|
198
220
|
target: ["chrome111", "edge111", "firefox111", "safari16.4"],
|
|
199
221
|
minify: true,
|
|
@@ -203,10 +225,15 @@ export async function buildClient(config, { watch = false } = {}) {
|
|
|
203
225
|
entryNames: "[name].[hash]",
|
|
204
226
|
chunkNames: "chunks/[name].[hash]",
|
|
205
227
|
legalComments: "none",
|
|
206
|
-
plugins
|
|
228
|
+
plugins,
|
|
207
229
|
define: {
|
|
208
230
|
"process.env": JSON.stringify(publicEnv(config.clientEnv)),
|
|
209
231
|
},
|
|
232
|
+
// Grup sarmalayıcısı her üyede `export { default }` yazar; named-only
|
|
233
|
+
// modüllerde bu uyarı gürültü — davranış yine doğru (default undefined).
|
|
234
|
+
...(groupPlugin
|
|
235
|
+
? { logOverride: { "import-is-undefined": "silent" } }
|
|
236
|
+
: {}),
|
|
210
237
|
};
|
|
211
238
|
|
|
212
239
|
if (watch) {
|
|
@@ -218,8 +245,7 @@ export async function buildClient(config, { watch = false } = {}) {
|
|
|
218
245
|
await context.watch();
|
|
219
246
|
writeMetafile(result.metafile);
|
|
220
247
|
const manifest = toManifest(result.metafile, config, entryRoot);
|
|
221
|
-
|
|
222
|
-
log.detail(`${entries} ${entries === 1 ? "entry" : "entries"}`);
|
|
248
|
+
log.detail(summarizeClientBuild(manifest, result.metafile));
|
|
223
249
|
return manifest;
|
|
224
250
|
}
|
|
225
251
|
|
|
@@ -230,11 +256,32 @@ export async function buildClient(config, { watch = false } = {}) {
|
|
|
230
256
|
(sum, output) => sum + output.bytes,
|
|
231
257
|
0,
|
|
232
258
|
);
|
|
233
|
-
|
|
234
|
-
|
|
259
|
+
log.detail(
|
|
260
|
+
`${summarizeClientBuild(manifest, result.metafile)}, ${log.size(bytes)}`,
|
|
261
|
+
);
|
|
235
262
|
return manifest;
|
|
236
263
|
}
|
|
237
264
|
|
|
265
|
+
/**
|
|
266
|
+
* Entry + inject edilen group entry'ler metafile'da durur; teşhis için
|
|
267
|
+
* hem gerçek manifest entry sayısını hem toplam çıktı dosyasını yaz.
|
|
268
|
+
*
|
|
269
|
+
* @param {Record<string, string>} manifest
|
|
270
|
+
* @param {esbuild.Metafile} [metafile]
|
|
271
|
+
* @returns {string}
|
|
272
|
+
*/
|
|
273
|
+
function summarizeClientBuild(manifest, metafile) {
|
|
274
|
+
const entries = Object.keys(manifest).length;
|
|
275
|
+
const entryLabel = `${entries} ${entries === 1 ? "entry" : "entries"}`;
|
|
276
|
+
if (!metafile) return entryLabel;
|
|
277
|
+
|
|
278
|
+
const outputs = Object.keys(metafile.outputs).filter(
|
|
279
|
+
(file) => file.endsWith(".js") || file.endsWith(".mjs"),
|
|
280
|
+
);
|
|
281
|
+
const chunks = outputs.length;
|
|
282
|
+
return `${entryLabel}, ${chunks} ${chunks === 1 ? "file" : "files"}`;
|
|
283
|
+
}
|
|
284
|
+
|
|
238
285
|
/**
|
|
239
286
|
* Metafile diske yazılır: dev panelindeki chunk analizi giriş/çıkış
|
|
240
287
|
* kırılımını buradan okur. Çalışma zamanı bu dosyaya bağımlı değildir.
|
package/src/config/defaults.js
CHANGED
|
@@ -470,6 +470,31 @@ export const DEFAULT_SECURITY = {
|
|
|
470
470
|
},
|
|
471
471
|
};
|
|
472
472
|
|
|
473
|
+
/**
|
|
474
|
+
* Client bundle (esbuild) ayarları.
|
|
475
|
+
*
|
|
476
|
+
* `groups` / `chunkGroup` esbuild'in native manualChunks'ı olmadığı için
|
|
477
|
+
* framework plugin'i ile uygulanır — ayrıntı `src/build/chunk-groups.mjs`.
|
|
478
|
+
*/
|
|
479
|
+
export const DEFAULT_BUILD = {
|
|
480
|
+
client: {
|
|
481
|
+
/** esbuild `splitting`; kapalıyken gruplama yok sayılır. */
|
|
482
|
+
splitting: true,
|
|
483
|
+
/**
|
|
484
|
+
* İsim → matcher listesi. Bare paket (`chart.js`), yol öneki
|
|
485
|
+
* (`./client/chrome/`) veya `@/` öneki.
|
|
486
|
+
* @type {Record<string, string[]>}
|
|
487
|
+
*/
|
|
488
|
+
groups: {},
|
|
489
|
+
/**
|
|
490
|
+
* Modül kimliğini (mutlak yol veya bare specifier) grup adına map eder.
|
|
491
|
+
* `groups` ile çakışınca hook kazanır.
|
|
492
|
+
* @type {((id: string) => string | null | undefined) | null}
|
|
493
|
+
*/
|
|
494
|
+
chunkGroup: null,
|
|
495
|
+
},
|
|
496
|
+
};
|
|
497
|
+
|
|
473
498
|
/**
|
|
474
499
|
* Build-zamanı görsel adımı + opsiyonel runtime uzak görsel proxy.
|
|
475
500
|
*
|
package/src/config/index.js
CHANGED
|
@@ -47,6 +47,7 @@ import {
|
|
|
47
47
|
ON_VISIT_CONCURRENCY_CEILING,
|
|
48
48
|
ON_VISIT_PER_PAGE_CEILING,
|
|
49
49
|
ON_VISIT_RPS_CEILING,
|
|
50
|
+
DEFAULT_BUILD,
|
|
50
51
|
DEFAULT_IMAGES,
|
|
51
52
|
DEFAULT_LOGS,
|
|
52
53
|
DEFAULT_NAVIGATION,
|
|
@@ -160,6 +161,17 @@ const CONFIG_FILE = "jskelet.config.mjs";
|
|
|
160
161
|
* @property {{ scan?: string[], dir: string } | false} icons
|
|
161
162
|
* @property {ImagesConfig | false} images
|
|
162
163
|
* @property {string[]} clientEnv Client bundle'a gömülecek env anahtarları.
|
|
164
|
+
* @property {BuildConfig} build Client bundling (esbuild splitting / chunk groups).
|
|
165
|
+
*/
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* @typedef {object} BuildClientConfig
|
|
169
|
+
* @property {boolean} splitting
|
|
170
|
+
* @property {Record<string, string[]>} groups
|
|
171
|
+
* @property {((id: string) => string | null | undefined) | null} chunkGroup
|
|
172
|
+
*
|
|
173
|
+
* @typedef {object} BuildConfig
|
|
174
|
+
* @property {BuildClientConfig} client
|
|
163
175
|
*/
|
|
164
176
|
|
|
165
177
|
/**
|
|
@@ -1139,6 +1151,118 @@ function normalizeSecurity(raw) {
|
|
|
1139
1151
|
};
|
|
1140
1152
|
}
|
|
1141
1153
|
|
|
1154
|
+
/**
|
|
1155
|
+
* Client build ayarları (`build.client`). Bozuk tipler uyarıyla varsayılana düşer.
|
|
1156
|
+
*
|
|
1157
|
+
* @param {unknown} raw
|
|
1158
|
+
* @returns {BuildConfig}
|
|
1159
|
+
*/
|
|
1160
|
+
export function normalizeBuild(raw) {
|
|
1161
|
+
if (raw == null) {
|
|
1162
|
+
return {
|
|
1163
|
+
client: {
|
|
1164
|
+
splitting: DEFAULT_BUILD.client.splitting,
|
|
1165
|
+
groups: {},
|
|
1166
|
+
chunkGroup: null,
|
|
1167
|
+
},
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
if (typeof raw !== "object" || Array.isArray(raw)) {
|
|
1172
|
+
console.warn("[config] build must be an object, ignoring it");
|
|
1173
|
+
return {
|
|
1174
|
+
client: {
|
|
1175
|
+
splitting: DEFAULT_BUILD.client.splitting,
|
|
1176
|
+
groups: {},
|
|
1177
|
+
chunkGroup: null,
|
|
1178
|
+
},
|
|
1179
|
+
};
|
|
1180
|
+
}
|
|
1181
|
+
|
|
1182
|
+
const source = /** @type {Record<string, any>} */ (raw);
|
|
1183
|
+
const clientRaw = source.client;
|
|
1184
|
+
|
|
1185
|
+
if (clientRaw == null) {
|
|
1186
|
+
return {
|
|
1187
|
+
client: {
|
|
1188
|
+
splitting: DEFAULT_BUILD.client.splitting,
|
|
1189
|
+
groups: {},
|
|
1190
|
+
chunkGroup: null,
|
|
1191
|
+
},
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
|
|
1195
|
+
if (typeof clientRaw !== "object" || Array.isArray(clientRaw)) {
|
|
1196
|
+
console.warn("[config] build.client must be an object, ignoring it");
|
|
1197
|
+
return {
|
|
1198
|
+
client: {
|
|
1199
|
+
splitting: DEFAULT_BUILD.client.splitting,
|
|
1200
|
+
groups: {},
|
|
1201
|
+
chunkGroup: null,
|
|
1202
|
+
},
|
|
1203
|
+
};
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
const client = /** @type {Record<string, any>} */ (clientRaw);
|
|
1207
|
+
const splitting = client.splitting !== false;
|
|
1208
|
+
|
|
1209
|
+
/** @type {Record<string, string[]>} */
|
|
1210
|
+
const groups = {};
|
|
1211
|
+
if (client.groups != null) {
|
|
1212
|
+
if (typeof client.groups !== "object" || Array.isArray(client.groups)) {
|
|
1213
|
+
console.warn("[config] build.client.groups must be an object, ignoring it");
|
|
1214
|
+
} else {
|
|
1215
|
+
for (const [name, matchers] of Object.entries(client.groups)) {
|
|
1216
|
+
if (!isSafeGroupName(name)) {
|
|
1217
|
+
console.warn(
|
|
1218
|
+
`[config] build.client.groups key "${name}" is invalid ` +
|
|
1219
|
+
"(use letters, digits, _ or -); skipping",
|
|
1220
|
+
);
|
|
1221
|
+
continue;
|
|
1222
|
+
}
|
|
1223
|
+
const list = asArray(matchers, `build.client.groups.${name}`)
|
|
1224
|
+
.filter((entry) => typeof entry === "string" && entry.trim())
|
|
1225
|
+
.map((entry) => String(entry).trim());
|
|
1226
|
+
if (list.length) groups[name] = list;
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
|
|
1231
|
+
/** @type {((id: string) => string | null | undefined) | null} */
|
|
1232
|
+
let chunkGroup = null;
|
|
1233
|
+
if (client.chunkGroup != null) {
|
|
1234
|
+
if (typeof client.chunkGroup === "function") {
|
|
1235
|
+
chunkGroup = client.chunkGroup;
|
|
1236
|
+
} else {
|
|
1237
|
+
console.warn(
|
|
1238
|
+
"[config] build.client.chunkGroup must be a function, ignoring it",
|
|
1239
|
+
);
|
|
1240
|
+
}
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
if (
|
|
1244
|
+
!splitting &&
|
|
1245
|
+
(Object.keys(groups).length > 0 || chunkGroup)
|
|
1246
|
+
) {
|
|
1247
|
+
console.warn(
|
|
1248
|
+
"[config] build.client.groups / chunkGroup require splitting; " +
|
|
1249
|
+
"they are ignored while splitting is false",
|
|
1250
|
+
);
|
|
1251
|
+
}
|
|
1252
|
+
|
|
1253
|
+
return { client: { splitting, groups, chunkGroup } };
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
/**
|
|
1257
|
+
* Chunk dosya adına güvenli grup adı.
|
|
1258
|
+
*
|
|
1259
|
+
* @param {string} name
|
|
1260
|
+
* @returns {boolean}
|
|
1261
|
+
*/
|
|
1262
|
+
function isSafeGroupName(name) {
|
|
1263
|
+
return typeof name === "string" && /^[A-Za-z0-9_-]+$/.test(name);
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1142
1266
|
/**
|
|
1143
1267
|
* İkon sprite ayarları. `false` → adım atlanır. `dir` varsayılanı `"icons"`:
|
|
1144
1268
|
* o dizin varsa yalnızca yerel SVG'ler; yoksa Phosphor.
|
|
@@ -1423,6 +1547,7 @@ export async function loadConfig(options = {}) {
|
|
|
1423
1547
|
icons: normalizeIcons(source.icons),
|
|
1424
1548
|
images: normalizeImages(source.images),
|
|
1425
1549
|
clientEnv: source.clientEnv ?? [],
|
|
1550
|
+
build: normalizeBuild(source.build),
|
|
1426
1551
|
};
|
|
1427
1552
|
|
|
1428
1553
|
if (
|