@lolyjs/core 0.2.0-alpha.13 → 0.2.0-alpha.15
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/README.md +99 -12
- package/dist/cli.cjs +207 -69
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +207 -69
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +207 -69
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +4 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +207 -69
- package/dist/index.js.map +1 -1
- package/dist/react/hooks.cjs +16 -6
- package/dist/react/hooks.cjs.map +1 -1
- package/dist/react/hooks.js +23 -13
- package/dist/react/hooks.js.map +1 -1
- package/dist/react/themes.cjs +61 -18
- package/dist/react/themes.cjs.map +1 -1
- package/dist/react/themes.js +63 -20
- package/dist/react/themes.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.mts
CHANGED
|
@@ -168,6 +168,10 @@ interface LoaderResult {
|
|
|
168
168
|
className?: string;
|
|
169
169
|
theme?: string;
|
|
170
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Server loader function type (getServerSideProps).
|
|
173
|
+
* This function is exported from server.hook.ts files.
|
|
174
|
+
*/
|
|
171
175
|
type ServerLoader = (ctx: ServerContext) => Promise<LoaderResult>;
|
|
172
176
|
interface PageMetadata {
|
|
173
177
|
title?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -168,6 +168,10 @@ interface LoaderResult {
|
|
|
168
168
|
className?: string;
|
|
169
169
|
theme?: string;
|
|
170
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Server loader function type (getServerSideProps).
|
|
173
|
+
* This function is exported from server.hook.ts files.
|
|
174
|
+
*/
|
|
171
175
|
type ServerLoader = (ctx: ServerContext) => Promise<LoaderResult>;
|
|
172
176
|
interface PageMetadata {
|
|
173
177
|
title?: string;
|
package/dist/index.js
CHANGED
|
@@ -204,7 +204,7 @@ function loadLayoutsForDir(pageDir, appDir) {
|
|
|
204
204
|
};
|
|
205
205
|
}
|
|
206
206
|
|
|
207
|
-
// modules/router/
|
|
207
|
+
// modules/router/server-hook.ts
|
|
208
208
|
import fs3 from "fs";
|
|
209
209
|
import path3 from "path";
|
|
210
210
|
var NAMING = {
|
|
@@ -216,14 +216,16 @@ var NAMING = {
|
|
|
216
216
|
// Files
|
|
217
217
|
SERVER_HOOK: "server.hook"
|
|
218
218
|
};
|
|
219
|
-
function
|
|
220
|
-
const
|
|
221
|
-
const
|
|
222
|
-
const
|
|
219
|
+
function loadServerHookForDir(currentDir) {
|
|
220
|
+
const pageServerHookTs = path3.join(currentDir, `page.server.hook.ts`);
|
|
221
|
+
const pageServerHookJs = path3.join(currentDir, `page.server.hook.js`);
|
|
222
|
+
const serverHookTs = path3.join(currentDir, `${NAMING.SERVER_HOOK}.ts`);
|
|
223
|
+
const serverHookJs = path3.join(currentDir, `${NAMING.SERVER_HOOK}.js`);
|
|
224
|
+
const file = fs3.existsSync(pageServerHookTs) ? pageServerHookTs : fs3.existsSync(pageServerHookJs) ? pageServerHookJs : fs3.existsSync(serverHookTs) ? serverHookTs : fs3.existsSync(serverHookJs) ? serverHookJs : null;
|
|
223
225
|
if (!file) {
|
|
224
226
|
return {
|
|
225
227
|
middlewares: [],
|
|
226
|
-
|
|
228
|
+
serverHook: null,
|
|
227
229
|
dynamic: "auto",
|
|
228
230
|
generateStaticParams: null
|
|
229
231
|
};
|
|
@@ -239,12 +241,12 @@ function loadLoaderForDir(currentDir) {
|
|
|
239
241
|
mod = __require(file);
|
|
240
242
|
} catch (error) {
|
|
241
243
|
console.error(
|
|
242
|
-
`[framework][
|
|
244
|
+
`[framework][server-hook] Error loading server hook from ${file}:`,
|
|
243
245
|
error
|
|
244
246
|
);
|
|
245
247
|
return {
|
|
246
248
|
middlewares: [],
|
|
247
|
-
|
|
249
|
+
serverHook: null,
|
|
248
250
|
dynamic: "auto",
|
|
249
251
|
generateStaticParams: null
|
|
250
252
|
};
|
|
@@ -252,16 +254,43 @@ function loadLoaderForDir(currentDir) {
|
|
|
252
254
|
const middlewares = Array.isArray(
|
|
253
255
|
mod?.[NAMING.BEFORE_MIDDLEWARES]
|
|
254
256
|
) ? mod[NAMING.BEFORE_MIDDLEWARES] : [];
|
|
255
|
-
const
|
|
257
|
+
const serverHook = typeof mod?.[NAMING.GET_SERVER_DATA_FN] === "function" ? mod[NAMING.GET_SERVER_DATA_FN] : null;
|
|
256
258
|
const dynamic = mod?.[NAMING.RENDER_TYPE_CONST] === "force-static" || mod?.[NAMING.RENDER_TYPE_CONST] === "force-dynamic" ? mod.dynamic : "auto";
|
|
257
259
|
const generateStaticParams = typeof mod?.[NAMING.GENERATE_SSG_PARAMS] === "function" ? mod[NAMING.GENERATE_SSG_PARAMS] : null;
|
|
258
260
|
return {
|
|
259
261
|
middlewares,
|
|
260
|
-
|
|
262
|
+
serverHook,
|
|
261
263
|
dynamic,
|
|
262
264
|
generateStaticParams
|
|
263
265
|
};
|
|
264
266
|
}
|
|
267
|
+
function loadLayoutServerHook(layoutFile) {
|
|
268
|
+
const layoutDir = path3.dirname(layoutFile);
|
|
269
|
+
const layoutBasename = path3.basename(layoutFile, path3.extname(layoutFile));
|
|
270
|
+
const serverHookTs = path3.join(layoutDir, `${layoutBasename}.server.hook.ts`);
|
|
271
|
+
const serverHookJs = path3.join(layoutDir, `${layoutBasename}.server.hook.js`);
|
|
272
|
+
const file = fs3.existsSync(serverHookTs) ? serverHookTs : fs3.existsSync(serverHookJs) ? serverHookJs : null;
|
|
273
|
+
if (!file) {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
if (file.endsWith(".ts") || file.endsWith(".tsx")) {
|
|
277
|
+
try {
|
|
278
|
+
__require("tsx/cjs");
|
|
279
|
+
} catch (e) {
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
try {
|
|
283
|
+
const mod = __require(file);
|
|
284
|
+
const serverHook = typeof mod?.getServerSideProps === "function" ? mod.getServerSideProps : null;
|
|
285
|
+
return serverHook;
|
|
286
|
+
} catch (error) {
|
|
287
|
+
console.error(
|
|
288
|
+
`[framework][server-hook] Error loading layout server hook from ${file}:`,
|
|
289
|
+
error
|
|
290
|
+
);
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
}
|
|
265
294
|
|
|
266
295
|
// modules/router/loader-pages.ts
|
|
267
296
|
function loadRoutes(appDir) {
|
|
@@ -293,7 +322,12 @@ function loadRoutes(appDir) {
|
|
|
293
322
|
currentDir,
|
|
294
323
|
appDir
|
|
295
324
|
);
|
|
296
|
-
const
|
|
325
|
+
const layoutServerHooks = [];
|
|
326
|
+
for (const layoutFile of layoutFiles) {
|
|
327
|
+
const layoutServerHook = loadLayoutServerHook(layoutFile);
|
|
328
|
+
layoutServerHooks.push(layoutServerHook);
|
|
329
|
+
}
|
|
330
|
+
const { middlewares, serverHook, dynamic, generateStaticParams } = loadServerHookForDir(currentDir);
|
|
297
331
|
routes.push({
|
|
298
332
|
pattern: routePath,
|
|
299
333
|
regex,
|
|
@@ -303,7 +337,10 @@ function loadRoutes(appDir) {
|
|
|
303
337
|
pageFile: fullPath,
|
|
304
338
|
layoutFiles,
|
|
305
339
|
middlewares,
|
|
306
|
-
loader,
|
|
340
|
+
loader: serverHook,
|
|
341
|
+
// Keep 'loader' field name for backward compatibility
|
|
342
|
+
layoutServerHooks,
|
|
343
|
+
// Server hooks for each layout (same order as layouts)
|
|
307
344
|
dynamic,
|
|
308
345
|
generateStaticParams
|
|
309
346
|
});
|
|
@@ -822,7 +859,12 @@ function loadRoutesFromManifest(projectRoot) {
|
|
|
822
859
|
(f) => path8.join(projectRoot, f)
|
|
823
860
|
);
|
|
824
861
|
const pageDir = path8.dirname(pageFile);
|
|
825
|
-
const
|
|
862
|
+
const layoutServerHooks = [];
|
|
863
|
+
for (const layoutFile of layoutFiles) {
|
|
864
|
+
const layoutServerHook = loadLayoutServerHook(layoutFile);
|
|
865
|
+
layoutServerHooks.push(layoutServerHook);
|
|
866
|
+
}
|
|
867
|
+
const { middlewares, serverHook, dynamic, generateStaticParams } = loadServerHookForDir(pageDir);
|
|
826
868
|
pageRoutes.push({
|
|
827
869
|
pattern: entry.pattern,
|
|
828
870
|
regex,
|
|
@@ -832,7 +874,10 @@ function loadRoutesFromManifest(projectRoot) {
|
|
|
832
874
|
pageFile,
|
|
833
875
|
layoutFiles,
|
|
834
876
|
middlewares,
|
|
835
|
-
loader,
|
|
877
|
+
loader: serverHook,
|
|
878
|
+
// Keep 'loader' field name for backward compatibility
|
|
879
|
+
layoutServerHooks,
|
|
880
|
+
// Server hooks for each layout (same order as layouts)
|
|
836
881
|
dynamic: entry.dynamic ?? dynamic,
|
|
837
882
|
generateStaticParams
|
|
838
883
|
});
|
|
@@ -1086,7 +1131,12 @@ function loadNotFoundRouteFromFilesystem(appDir) {
|
|
|
1086
1131
|
notFoundDir,
|
|
1087
1132
|
appDir
|
|
1088
1133
|
);
|
|
1089
|
-
const
|
|
1134
|
+
const layoutServerHooks = [];
|
|
1135
|
+
for (const layoutFile of layoutFiles) {
|
|
1136
|
+
const layoutServerHook = loadLayoutServerHook(layoutFile);
|
|
1137
|
+
layoutServerHooks.push(layoutServerHook);
|
|
1138
|
+
}
|
|
1139
|
+
const { middlewares, serverHook, dynamic, generateStaticParams } = loadServerHookForDir(notFoundDir);
|
|
1090
1140
|
return {
|
|
1091
1141
|
pattern: NOT_FOUND_PATTERN,
|
|
1092
1142
|
regex: new RegExp(`^${NOT_FOUND_PATTERN}/?$`),
|
|
@@ -1096,7 +1146,10 @@ function loadNotFoundRouteFromFilesystem(appDir) {
|
|
|
1096
1146
|
pageFile: notFoundFile,
|
|
1097
1147
|
layoutFiles,
|
|
1098
1148
|
middlewares,
|
|
1099
|
-
loader,
|
|
1149
|
+
loader: serverHook,
|
|
1150
|
+
// Keep 'loader' field name for backward compatibility
|
|
1151
|
+
layoutServerHooks,
|
|
1152
|
+
// Server hooks for each layout (same order as layouts)
|
|
1100
1153
|
dynamic,
|
|
1101
1154
|
generateStaticParams
|
|
1102
1155
|
};
|
|
@@ -1127,7 +1180,12 @@ function loadErrorRouteFromFilesystem(appDir) {
|
|
|
1127
1180
|
appDir,
|
|
1128
1181
|
appDir
|
|
1129
1182
|
);
|
|
1130
|
-
const
|
|
1183
|
+
const layoutServerHooks = [];
|
|
1184
|
+
for (const layoutFile of layoutFiles) {
|
|
1185
|
+
const layoutServerHook = loadLayoutServerHook(layoutFile);
|
|
1186
|
+
layoutServerHooks.push(layoutServerHook);
|
|
1187
|
+
}
|
|
1188
|
+
const { middlewares, serverHook, dynamic, generateStaticParams } = loadServerHookForDir(appDir);
|
|
1131
1189
|
return {
|
|
1132
1190
|
pattern: ERROR_PATTERN,
|
|
1133
1191
|
regex: new RegExp(`^${ERROR_PATTERN}/?$`),
|
|
@@ -1137,7 +1195,10 @@ function loadErrorRouteFromFilesystem(appDir) {
|
|
|
1137
1195
|
pageFile: errorFile,
|
|
1138
1196
|
layoutFiles,
|
|
1139
1197
|
middlewares,
|
|
1140
|
-
loader,
|
|
1198
|
+
loader: serverHook,
|
|
1199
|
+
// Keep 'loader' field name for backward compatibility
|
|
1200
|
+
layoutServerHooks,
|
|
1201
|
+
// Server hooks for each layout (same order as layouts)
|
|
1141
1202
|
dynamic,
|
|
1142
1203
|
generateStaticParams
|
|
1143
1204
|
};
|
|
@@ -3711,7 +3772,6 @@ function requestLoggerMiddleware(options = {}) {
|
|
|
3711
3772
|
const startTime = Date.now();
|
|
3712
3773
|
res.on("finish", () => {
|
|
3713
3774
|
const duration = Date.now() - startTime;
|
|
3714
|
-
const shouldLogSuccess = logRequests === true;
|
|
3715
3775
|
if (res.statusCode >= 500) {
|
|
3716
3776
|
reqLogger.error(`${req.method} ${req.path} ${res.statusCode}`, {
|
|
3717
3777
|
statusCode: res.statusCode,
|
|
@@ -3722,11 +3782,6 @@ function requestLoggerMiddleware(options = {}) {
|
|
|
3722
3782
|
statusCode: res.statusCode,
|
|
3723
3783
|
duration: `${duration}ms`
|
|
3724
3784
|
});
|
|
3725
|
-
} else if (shouldLogSuccess) {
|
|
3726
|
-
reqLogger.debug(`${req.method} ${req.path} ${res.statusCode}`, {
|
|
3727
|
-
statusCode: res.statusCode,
|
|
3728
|
-
duration: `${duration}ms`
|
|
3729
|
-
});
|
|
3730
3785
|
}
|
|
3731
3786
|
});
|
|
3732
3787
|
}
|
|
@@ -3983,8 +4038,8 @@ async function runRouteMiddlewares(route, ctx) {
|
|
|
3983
4038
|
}
|
|
3984
4039
|
}
|
|
3985
4040
|
|
|
3986
|
-
// modules/server/handlers/
|
|
3987
|
-
async function
|
|
4041
|
+
// modules/server/handlers/server-hook.ts
|
|
4042
|
+
async function runRouteServerHook(route, ctx) {
|
|
3988
4043
|
if (!route.loader) {
|
|
3989
4044
|
return { props: {} };
|
|
3990
4045
|
}
|
|
@@ -4141,11 +4196,39 @@ async function handlePageRequestInternal(options) {
|
|
|
4141
4196
|
pathname: urlPath,
|
|
4142
4197
|
locals: {}
|
|
4143
4198
|
};
|
|
4144
|
-
|
|
4199
|
+
const layoutProps2 = {};
|
|
4200
|
+
if (notFoundPage.layoutServerHooks && notFoundPage.layoutServerHooks.length > 0) {
|
|
4201
|
+
for (let i = 0; i < notFoundPage.layoutServerHooks.length; i++) {
|
|
4202
|
+
const layoutServerHook = notFoundPage.layoutServerHooks[i];
|
|
4203
|
+
if (layoutServerHook) {
|
|
4204
|
+
try {
|
|
4205
|
+
const layoutResult = await layoutServerHook(ctx2);
|
|
4206
|
+
if (layoutResult.props) {
|
|
4207
|
+
Object.assign(layoutProps2, layoutResult.props);
|
|
4208
|
+
}
|
|
4209
|
+
} catch (error) {
|
|
4210
|
+
const reqLogger2 = getRequestLogger(req);
|
|
4211
|
+
reqLogger2.warn(`Layout server hook ${i} failed for not-found`, {
|
|
4212
|
+
error,
|
|
4213
|
+
layoutFile: notFoundPage.layoutFiles[i]
|
|
4214
|
+
});
|
|
4215
|
+
}
|
|
4216
|
+
}
|
|
4217
|
+
}
|
|
4218
|
+
}
|
|
4219
|
+
let loaderResult2 = await runRouteServerHook(notFoundPage, ctx2);
|
|
4145
4220
|
if (!loaderResult2.theme) {
|
|
4146
4221
|
loaderResult2.theme = theme;
|
|
4147
4222
|
}
|
|
4148
|
-
const
|
|
4223
|
+
const combinedProps2 = {
|
|
4224
|
+
...layoutProps2,
|
|
4225
|
+
...loaderResult2.props || {}
|
|
4226
|
+
};
|
|
4227
|
+
const combinedLoaderResult2 = {
|
|
4228
|
+
...loaderResult2,
|
|
4229
|
+
props: combinedProps2
|
|
4230
|
+
};
|
|
4231
|
+
const initialData2 = buildInitialData(urlPath, {}, combinedLoaderResult2);
|
|
4149
4232
|
const appTree2 = buildAppTree(notFoundPage, {}, initialData2.props);
|
|
4150
4233
|
initialData2.notFound = true;
|
|
4151
4234
|
const nonce2 = res.locals.nonce || void 0;
|
|
@@ -4153,7 +4236,7 @@ async function handlePageRequestInternal(options) {
|
|
|
4153
4236
|
appTree: appTree2,
|
|
4154
4237
|
initialData: initialData2,
|
|
4155
4238
|
routerData,
|
|
4156
|
-
meta:
|
|
4239
|
+
meta: combinedLoaderResult2.metadata ?? null,
|
|
4157
4240
|
titleFallback: "Not found",
|
|
4158
4241
|
descriptionFallback: "Loly demo",
|
|
4159
4242
|
chunkHref: null,
|
|
@@ -4172,8 +4255,8 @@ async function handlePageRequestInternal(options) {
|
|
|
4172
4255
|
},
|
|
4173
4256
|
onShellError(err) {
|
|
4174
4257
|
didError2 = true;
|
|
4175
|
-
const
|
|
4176
|
-
|
|
4258
|
+
const reqLogger2 = getRequestLogger(req);
|
|
4259
|
+
reqLogger2.error("SSR shell error", err, { route: "not-found" });
|
|
4177
4260
|
if (!res.headersSent && errorPage) {
|
|
4178
4261
|
renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env);
|
|
4179
4262
|
} else if (!res.headersSent) {
|
|
@@ -4185,8 +4268,8 @@ async function handlePageRequestInternal(options) {
|
|
|
4185
4268
|
},
|
|
4186
4269
|
onError(err) {
|
|
4187
4270
|
didError2 = true;
|
|
4188
|
-
const
|
|
4189
|
-
|
|
4271
|
+
const reqLogger2 = getRequestLogger(req);
|
|
4272
|
+
reqLogger2.error("SSR error", err, { route: "not-found" });
|
|
4190
4273
|
}
|
|
4191
4274
|
});
|
|
4192
4275
|
req.on("close", () => abort2());
|
|
@@ -4208,9 +4291,30 @@ async function handlePageRequestInternal(options) {
|
|
|
4208
4291
|
if (res.headersSent) {
|
|
4209
4292
|
return;
|
|
4210
4293
|
}
|
|
4294
|
+
const layoutProps = {};
|
|
4295
|
+
const reqLogger = getRequestLogger(req);
|
|
4296
|
+
if (route.layoutServerHooks && route.layoutServerHooks.length > 0) {
|
|
4297
|
+
for (let i = 0; i < route.layoutServerHooks.length; i++) {
|
|
4298
|
+
const layoutServerHook = route.layoutServerHooks[i];
|
|
4299
|
+
if (layoutServerHook) {
|
|
4300
|
+
try {
|
|
4301
|
+
const layoutResult = await layoutServerHook(ctx);
|
|
4302
|
+
if (layoutResult.props) {
|
|
4303
|
+
Object.assign(layoutProps, layoutResult.props);
|
|
4304
|
+
}
|
|
4305
|
+
} catch (error) {
|
|
4306
|
+
reqLogger.warn(`Layout server hook ${i} failed`, {
|
|
4307
|
+
error,
|
|
4308
|
+
layoutFile: route.layoutFiles[i],
|
|
4309
|
+
route: route.pattern
|
|
4310
|
+
});
|
|
4311
|
+
}
|
|
4312
|
+
}
|
|
4313
|
+
}
|
|
4314
|
+
}
|
|
4211
4315
|
let loaderResult;
|
|
4212
4316
|
try {
|
|
4213
|
-
loaderResult = await
|
|
4317
|
+
loaderResult = await runRouteServerHook(route, ctx);
|
|
4214
4318
|
if (!loaderResult.theme) {
|
|
4215
4319
|
loaderResult.theme = theme;
|
|
4216
4320
|
}
|
|
@@ -4229,8 +4333,18 @@ async function handlePageRequestInternal(options) {
|
|
|
4229
4333
|
}
|
|
4230
4334
|
}
|
|
4231
4335
|
}
|
|
4336
|
+
const combinedProps = {
|
|
4337
|
+
...layoutProps,
|
|
4338
|
+
// Props from layouts (stable)
|
|
4339
|
+
...loaderResult.props || {}
|
|
4340
|
+
// Props from page (overrides layout)
|
|
4341
|
+
};
|
|
4342
|
+
const combinedLoaderResult = {
|
|
4343
|
+
...loaderResult,
|
|
4344
|
+
props: combinedProps
|
|
4345
|
+
};
|
|
4232
4346
|
if (isDataReq) {
|
|
4233
|
-
handleDataResponse(res,
|
|
4347
|
+
handleDataResponse(res, combinedLoaderResult, theme);
|
|
4234
4348
|
return;
|
|
4235
4349
|
}
|
|
4236
4350
|
if (loaderResult.redirect) {
|
|
@@ -4245,7 +4359,7 @@ async function handlePageRequestInternal(options) {
|
|
|
4245
4359
|
}
|
|
4246
4360
|
return;
|
|
4247
4361
|
}
|
|
4248
|
-
const initialData = buildInitialData(urlPath, params,
|
|
4362
|
+
const initialData = buildInitialData(urlPath, params, combinedLoaderResult);
|
|
4249
4363
|
const appTree = buildAppTree(route, params, initialData.props);
|
|
4250
4364
|
const chunkName = routeChunks[route.pattern];
|
|
4251
4365
|
let chunkHref = null;
|
|
@@ -4261,7 +4375,7 @@ async function handlePageRequestInternal(options) {
|
|
|
4261
4375
|
appTree,
|
|
4262
4376
|
initialData,
|
|
4263
4377
|
routerData,
|
|
4264
|
-
meta:
|
|
4378
|
+
meta: combinedLoaderResult.metadata,
|
|
4265
4379
|
titleFallback: "Loly framework",
|
|
4266
4380
|
descriptionFallback: "Loly demo",
|
|
4267
4381
|
chunkHref,
|
|
@@ -4282,8 +4396,8 @@ async function handlePageRequestInternal(options) {
|
|
|
4282
4396
|
},
|
|
4283
4397
|
onShellError(err) {
|
|
4284
4398
|
didError = true;
|
|
4285
|
-
const
|
|
4286
|
-
|
|
4399
|
+
const reqLogger2 = getRequestLogger(req);
|
|
4400
|
+
reqLogger2.error("SSR shell error", err, { route: matched?.route?.pattern || "unknown" });
|
|
4287
4401
|
if (!res.headersSent && errorPage) {
|
|
4288
4402
|
renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env);
|
|
4289
4403
|
} else if (!res.headersSent) {
|
|
@@ -4295,8 +4409,8 @@ async function handlePageRequestInternal(options) {
|
|
|
4295
4409
|
},
|
|
4296
4410
|
onError(err) {
|
|
4297
4411
|
didError = true;
|
|
4298
|
-
const
|
|
4299
|
-
|
|
4412
|
+
const reqLogger2 = getRequestLogger(req);
|
|
4413
|
+
reqLogger2.error("SSR error", err, { route: matched?.route?.pattern || "unknown" });
|
|
4300
4414
|
}
|
|
4301
4415
|
});
|
|
4302
4416
|
req.on("close", () => {
|
|
@@ -4313,11 +4427,39 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
|
|
|
4313
4427
|
pathname: req.path,
|
|
4314
4428
|
locals: { error }
|
|
4315
4429
|
};
|
|
4316
|
-
|
|
4430
|
+
const layoutProps = {};
|
|
4431
|
+
const reqLogger = getRequestLogger(req);
|
|
4432
|
+
if (errorPage.layoutServerHooks && errorPage.layoutServerHooks.length > 0) {
|
|
4433
|
+
for (let i = 0; i < errorPage.layoutServerHooks.length; i++) {
|
|
4434
|
+
const layoutServerHook = errorPage.layoutServerHooks[i];
|
|
4435
|
+
if (layoutServerHook) {
|
|
4436
|
+
try {
|
|
4437
|
+
const layoutResult = await layoutServerHook(ctx);
|
|
4438
|
+
if (layoutResult.props) {
|
|
4439
|
+
Object.assign(layoutProps, layoutResult.props);
|
|
4440
|
+
}
|
|
4441
|
+
} catch (err) {
|
|
4442
|
+
reqLogger.warn(`Layout server hook ${i} failed for error page`, {
|
|
4443
|
+
error: err,
|
|
4444
|
+
layoutFile: errorPage.layoutFiles[i]
|
|
4445
|
+
});
|
|
4446
|
+
}
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
}
|
|
4450
|
+
let loaderResult = await runRouteServerHook(errorPage, ctx);
|
|
4317
4451
|
if (!loaderResult.theme && theme) {
|
|
4318
4452
|
loaderResult.theme = theme;
|
|
4319
4453
|
}
|
|
4320
|
-
const
|
|
4454
|
+
const combinedProps = {
|
|
4455
|
+
...layoutProps,
|
|
4456
|
+
...loaderResult.props || {}
|
|
4457
|
+
};
|
|
4458
|
+
const combinedLoaderResult = {
|
|
4459
|
+
...loaderResult,
|
|
4460
|
+
props: combinedProps
|
|
4461
|
+
};
|
|
4462
|
+
const initialData = buildInitialData(req.path, { error: String(error) }, combinedLoaderResult);
|
|
4321
4463
|
const routerData = buildRouterData(req);
|
|
4322
4464
|
initialData.error = true;
|
|
4323
4465
|
if (isDataReq) {
|
|
@@ -4327,8 +4469,8 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
|
|
|
4327
4469
|
error: true,
|
|
4328
4470
|
message: String(error),
|
|
4329
4471
|
props: initialData.props,
|
|
4330
|
-
metadata:
|
|
4331
|
-
theme:
|
|
4472
|
+
metadata: combinedLoaderResult.metadata ?? null,
|
|
4473
|
+
theme: combinedLoaderResult.theme ?? theme ?? null
|
|
4332
4474
|
}));
|
|
4333
4475
|
return;
|
|
4334
4476
|
}
|
|
@@ -4350,7 +4492,7 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
|
|
|
4350
4492
|
appTree,
|
|
4351
4493
|
initialData,
|
|
4352
4494
|
routerData,
|
|
4353
|
-
meta:
|
|
4495
|
+
meta: combinedLoaderResult.metadata ?? null,
|
|
4354
4496
|
titleFallback: "Error",
|
|
4355
4497
|
descriptionFallback: "An error occurred",
|
|
4356
4498
|
chunkHref,
|
|
@@ -4371,8 +4513,8 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
|
|
|
4371
4513
|
},
|
|
4372
4514
|
onShellError(err) {
|
|
4373
4515
|
didError = true;
|
|
4374
|
-
const
|
|
4375
|
-
|
|
4516
|
+
const reqLogger2 = getRequestLogger(req);
|
|
4517
|
+
reqLogger2.error("Error rendering error page", err, { type: "shellError" });
|
|
4376
4518
|
if (!res.headersSent) {
|
|
4377
4519
|
res.statusCode = 500;
|
|
4378
4520
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
@@ -4382,8 +4524,8 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
|
|
|
4382
4524
|
},
|
|
4383
4525
|
onError(err) {
|
|
4384
4526
|
didError = true;
|
|
4385
|
-
const
|
|
4386
|
-
|
|
4527
|
+
const reqLogger2 = getRequestLogger(req);
|
|
4528
|
+
reqLogger2.error("Error in error page", err);
|
|
4387
4529
|
}
|
|
4388
4530
|
});
|
|
4389
4531
|
req.on("close", () => {
|
|
@@ -4824,26 +4966,22 @@ async function startServer(options = {}) {
|
|
|
4824
4966
|
});
|
|
4825
4967
|
httpServer.listen(port, host, () => {
|
|
4826
4968
|
if (isDev) {
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4969
|
+
const reset = "\x1B[0m";
|
|
4970
|
+
const cyan = "\x1B[36m";
|
|
4971
|
+
const green = "\x1B[32m";
|
|
4972
|
+
const dim = "\x1B[2m";
|
|
4973
|
+
const bold = "\x1B[1m";
|
|
4974
|
+
const url = `http://${host === "0.0.0.0" ? "localhost" : host}:${port}`;
|
|
4975
|
+
console.log();
|
|
4976
|
+
console.log(`${bold}${green}\u2713${reset} ${bold}Dev server ready${reset}`);
|
|
4977
|
+
console.log(`${dim} Local:${reset} ${cyan}${url}${reset}`);
|
|
4978
|
+
if (routes.length > 0 || apiRoutes.length > 0 || wssRoutes.length > 0) {
|
|
4979
|
+
console.log(`${dim} Routes:${reset} ${routes.length} pages, ${apiRoutes.length} API, ${wssRoutes.length} WSS`);
|
|
4980
|
+
}
|
|
4981
|
+
console.log();
|
|
4833
4982
|
} else {
|
|
4834
|
-
const
|
|
4835
|
-
|
|
4836
|
-
url: `http://${host}:${port}`,
|
|
4837
|
-
appDir,
|
|
4838
|
-
buildDir
|
|
4839
|
-
});
|
|
4840
|
-
logger3.info("\u{1F9ED} Reading compiled routes from", { appDir });
|
|
4841
|
-
logger3.info("\u{1F4E6} Client served from", {
|
|
4842
|
-
path: `/static (${buildDir}/client)`
|
|
4843
|
-
});
|
|
4844
|
-
logger3.info("\u{1F4C4} SSG served from", {
|
|
4845
|
-
path: `${buildDir}/ssg (if exists)`
|
|
4846
|
-
});
|
|
4983
|
+
const url = `http://${host}:${port}`;
|
|
4984
|
+
console.log(`\u{1F680} Server running on ${url}`);
|
|
4847
4985
|
}
|
|
4848
4986
|
});
|
|
4849
4987
|
}
|