@lolyjs/core 0.2.0-alpha.30 → 0.2.0-alpha.32
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 +6 -7
- package/dist/cli.cjs +359 -171
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +359 -171
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +363 -171
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +361 -171
- package/dist/index.js.map +1 -1
- package/dist/{index.types-B9j4OQft.d.mts → index.types-JJ0KjvFU.d.mts} +58 -14
- package/dist/{index.types-B9j4OQft.d.ts → index.types-JJ0KjvFU.d.ts} +58 -14
- package/dist/react/cache.cjs.map +1 -1
- package/dist/react/cache.d.mts +1 -1
- package/dist/react/cache.d.ts +1 -1
- package/dist/react/cache.js.map +1 -1
- package/dist/react/components.cjs.map +1 -1
- package/dist/react/components.js.map +1 -1
- package/dist/react/hooks.cjs.map +1 -1
- package/dist/react/hooks.js.map +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -10736,6 +10736,18 @@ function writeRoutesManifest({
|
|
|
10736
10736
|
import_fs6.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
10737
10737
|
}
|
|
10738
10738
|
|
|
10739
|
+
// modules/router/index.types.ts
|
|
10740
|
+
var RedirectResponse = class {
|
|
10741
|
+
constructor(destination, permanent = false) {
|
|
10742
|
+
this.destination = destination;
|
|
10743
|
+
this.permanent = permanent;
|
|
10744
|
+
}
|
|
10745
|
+
};
|
|
10746
|
+
var NotFoundResponse = class {
|
|
10747
|
+
constructor() {
|
|
10748
|
+
}
|
|
10749
|
+
};
|
|
10750
|
+
|
|
10739
10751
|
// modules/router/loader-routes.ts
|
|
10740
10752
|
var import_fs7 = __toESM(require("fs"));
|
|
10741
10753
|
var import_path9 = __toESM(require("path"));
|
|
@@ -13112,16 +13124,6 @@ async function runRouteServerHook(route, ctx) {
|
|
|
13112
13124
|
// modules/server/handlers/response.ts
|
|
13113
13125
|
function handleDataResponse(res, loaderResult, theme, layoutProps, pageProps, error, message) {
|
|
13114
13126
|
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
13115
|
-
if (loaderResult.redirect) {
|
|
13116
|
-
res.statusCode = 200;
|
|
13117
|
-
res.end(JSON.stringify({ redirect: loaderResult.redirect }));
|
|
13118
|
-
return;
|
|
13119
|
-
}
|
|
13120
|
-
if (loaderResult.notFound) {
|
|
13121
|
-
res.statusCode = 404;
|
|
13122
|
-
res.end(JSON.stringify({ notFound: true }));
|
|
13123
|
-
return;
|
|
13124
|
-
}
|
|
13125
13127
|
const response = {
|
|
13126
13128
|
// Combined props for backward compatibility
|
|
13127
13129
|
props: loaderResult.props ?? {},
|
|
@@ -13296,6 +13298,145 @@ function mergeMetadata(base, override) {
|
|
|
13296
13298
|
function isDataRequest(req) {
|
|
13297
13299
|
return req.query && req.query.__fw_data === "1" || req.headers["x-fw-data"] === "1";
|
|
13298
13300
|
}
|
|
13301
|
+
async function renderNotFoundPage(notFoundPage, req, res, urlPath, finalUrlPath, routerData, assetManifest, theme, clientJsPath, clientCssPath, faviconInfo, projectRoot, skipLayoutHooks, errorPage, routeChunks, env, config) {
|
|
13302
|
+
const ctx = {
|
|
13303
|
+
req,
|
|
13304
|
+
res,
|
|
13305
|
+
params: {},
|
|
13306
|
+
pathname: urlPath,
|
|
13307
|
+
locals: {},
|
|
13308
|
+
Redirect: (destination, permanent = false) => new RedirectResponse(destination, permanent),
|
|
13309
|
+
NotFound: () => new NotFoundResponse()
|
|
13310
|
+
};
|
|
13311
|
+
const layoutProps = {};
|
|
13312
|
+
if (!skipLayoutHooks && notFoundPage.layoutServerHooks && notFoundPage.layoutServerHooks.length > 0) {
|
|
13313
|
+
for (let i = 0; i < notFoundPage.layoutServerHooks.length; i++) {
|
|
13314
|
+
const layoutServerHook = notFoundPage.layoutServerHooks[i];
|
|
13315
|
+
const layoutMiddlewares = notFoundPage.layoutMiddlewares?.[i] || [];
|
|
13316
|
+
if (layoutMiddlewares.length > 0) {
|
|
13317
|
+
for (const mw of layoutMiddlewares) {
|
|
13318
|
+
try {
|
|
13319
|
+
await Promise.resolve(
|
|
13320
|
+
mw(ctx, async () => {
|
|
13321
|
+
})
|
|
13322
|
+
);
|
|
13323
|
+
} catch (error) {
|
|
13324
|
+
const reqLogger = getRequestLogger(req);
|
|
13325
|
+
const layoutFile = notFoundPage.layoutFiles[i];
|
|
13326
|
+
const relativeLayoutPath = layoutFile ? import_path21.default.relative(projectRoot || process.cwd(), layoutFile) : "unknown";
|
|
13327
|
+
reqLogger.error("Layout middleware failed for not-found page", error instanceof Error ? error : new Error(String(error)), {
|
|
13328
|
+
layoutIndex: i,
|
|
13329
|
+
layoutFile: relativeLayoutPath
|
|
13330
|
+
});
|
|
13331
|
+
throw error;
|
|
13332
|
+
}
|
|
13333
|
+
if (ctx.res.headersSent) {
|
|
13334
|
+
return;
|
|
13335
|
+
}
|
|
13336
|
+
}
|
|
13337
|
+
}
|
|
13338
|
+
if (layoutServerHook) {
|
|
13339
|
+
try {
|
|
13340
|
+
const layoutResult = await layoutServerHook(ctx);
|
|
13341
|
+
if (layoutResult instanceof RedirectResponse) {
|
|
13342
|
+
handleRedirect(res, { destination: layoutResult.destination, permanent: layoutResult.permanent });
|
|
13343
|
+
return;
|
|
13344
|
+
}
|
|
13345
|
+
if (layoutResult instanceof NotFoundResponse) {
|
|
13346
|
+
handleNotFound(res, urlPath);
|
|
13347
|
+
return;
|
|
13348
|
+
}
|
|
13349
|
+
if (layoutResult.props) {
|
|
13350
|
+
Object.assign(layoutProps, layoutResult.props);
|
|
13351
|
+
}
|
|
13352
|
+
} catch (error) {
|
|
13353
|
+
const reqLogger = getRequestLogger(req);
|
|
13354
|
+
const layoutFile = notFoundPage.layoutFiles[i];
|
|
13355
|
+
const relativeLayoutPath = layoutFile ? import_path21.default.relative(projectRoot || process.cwd(), layoutFile) : "unknown";
|
|
13356
|
+
reqLogger.warn("Layout server hook failed for not-found page", {
|
|
13357
|
+
error: error instanceof Error ? error.message : String(error),
|
|
13358
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
13359
|
+
layoutFile: relativeLayoutPath,
|
|
13360
|
+
layoutIndex: i
|
|
13361
|
+
});
|
|
13362
|
+
}
|
|
13363
|
+
}
|
|
13364
|
+
}
|
|
13365
|
+
}
|
|
13366
|
+
let loaderResult = await runRouteServerHook(notFoundPage, ctx);
|
|
13367
|
+
if (loaderResult instanceof RedirectResponse) {
|
|
13368
|
+
handleRedirect(res, { destination: loaderResult.destination, permanent: loaderResult.permanent });
|
|
13369
|
+
return;
|
|
13370
|
+
}
|
|
13371
|
+
if (loaderResult instanceof NotFoundResponse) {
|
|
13372
|
+
handleNotFound(res, urlPath);
|
|
13373
|
+
return;
|
|
13374
|
+
}
|
|
13375
|
+
const notFoundLoaderResult = loaderResult;
|
|
13376
|
+
if (!notFoundLoaderResult.theme) {
|
|
13377
|
+
notFoundLoaderResult.theme = theme;
|
|
13378
|
+
}
|
|
13379
|
+
const combinedProps = {
|
|
13380
|
+
...layoutProps,
|
|
13381
|
+
...notFoundLoaderResult.props || {}
|
|
13382
|
+
};
|
|
13383
|
+
const combinedLoaderResult = {
|
|
13384
|
+
...notFoundLoaderResult,
|
|
13385
|
+
props: combinedProps
|
|
13386
|
+
};
|
|
13387
|
+
const initialData = buildInitialData(finalUrlPath, {}, combinedLoaderResult);
|
|
13388
|
+
const appTree = buildAppTree(notFoundPage, {}, initialData.props);
|
|
13389
|
+
initialData.notFound = true;
|
|
13390
|
+
const nonce = res.locals.nonce || void 0;
|
|
13391
|
+
const entrypointFiles = [];
|
|
13392
|
+
if (assetManifest?.entrypoints?.client) {
|
|
13393
|
+
entrypointFiles.push(...assetManifest.entrypoints.client.map((file) => `${STATIC_PATH}/${file}`));
|
|
13394
|
+
}
|
|
13395
|
+
const documentTree = createDocumentTree({
|
|
13396
|
+
appTree,
|
|
13397
|
+
initialData,
|
|
13398
|
+
routerData,
|
|
13399
|
+
meta: combinedLoaderResult.metadata ?? null,
|
|
13400
|
+
titleFallback: "Not found",
|
|
13401
|
+
descriptionFallback: "Loly demo",
|
|
13402
|
+
chunkHref: null,
|
|
13403
|
+
entrypointFiles,
|
|
13404
|
+
theme,
|
|
13405
|
+
clientJsPath,
|
|
13406
|
+
clientCssPath,
|
|
13407
|
+
nonce,
|
|
13408
|
+
faviconPath: faviconInfo?.path || null,
|
|
13409
|
+
faviconType: faviconInfo?.type || null
|
|
13410
|
+
});
|
|
13411
|
+
let didError = false;
|
|
13412
|
+
const { pipe, abort } = (0, import_server.renderToPipeableStream)(documentTree, {
|
|
13413
|
+
onShellReady() {
|
|
13414
|
+
if (didError || res.headersSent) return;
|
|
13415
|
+
res.statusCode = 404;
|
|
13416
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
13417
|
+
pipe(res);
|
|
13418
|
+
},
|
|
13419
|
+
onShellError(err) {
|
|
13420
|
+
didError = true;
|
|
13421
|
+
const reqLogger = getRequestLogger(req);
|
|
13422
|
+
reqLogger.error("SSR shell error", err, { route: "not-found" });
|
|
13423
|
+
if (!res.headersSent && errorPage) {
|
|
13424
|
+
renderErrorPageWithStream(errorPage, req, res, err, routeChunks || {}, theme, projectRoot, env, config, notFoundPage);
|
|
13425
|
+
} else if (!res.headersSent) {
|
|
13426
|
+
res.statusCode = 500;
|
|
13427
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
13428
|
+
res.end("<!doctype html><h1>Internal Server Error</h1>");
|
|
13429
|
+
}
|
|
13430
|
+
abort();
|
|
13431
|
+
},
|
|
13432
|
+
onError(err) {
|
|
13433
|
+
didError = true;
|
|
13434
|
+
const reqLogger = getRequestLogger(req);
|
|
13435
|
+
reqLogger.error("SSR error", err, { route: "not-found" });
|
|
13436
|
+
}
|
|
13437
|
+
});
|
|
13438
|
+
req.on("close", () => abort());
|
|
13439
|
+
}
|
|
13299
13440
|
async function handlePageRequest(options) {
|
|
13300
13441
|
try {
|
|
13301
13442
|
await handlePageRequestInternal(options);
|
|
@@ -13303,7 +13444,7 @@ async function handlePageRequest(options) {
|
|
|
13303
13444
|
const { errorPage, req, res, routeChunks, theme, projectRoot } = options;
|
|
13304
13445
|
const reqLogger = getRequestLogger(req);
|
|
13305
13446
|
if (errorPage) {
|
|
13306
|
-
await renderErrorPageWithStream(errorPage, req, res, error, routeChunks || {}, theme, projectRoot, options.env, options.config);
|
|
13447
|
+
await renderErrorPageWithStream(errorPage, req, res, error, routeChunks || {}, theme, projectRoot, options.env, options.config, options.notFoundPage);
|
|
13307
13448
|
} else {
|
|
13308
13449
|
reqLogger.error("Unhandled error in page request", error, {
|
|
13309
13450
|
urlPath: options.urlPath,
|
|
@@ -13413,135 +13554,25 @@ async function handlePageRequestInternal(options) {
|
|
|
13413
13554
|
return;
|
|
13414
13555
|
}
|
|
13415
13556
|
if (notFoundPage) {
|
|
13416
|
-
|
|
13557
|
+
await renderNotFoundPage(
|
|
13558
|
+
notFoundPage,
|
|
13417
13559
|
req,
|
|
13418
13560
|
res,
|
|
13419
|
-
|
|
13420
|
-
|
|
13421
|
-
locals: {}
|
|
13422
|
-
};
|
|
13423
|
-
const layoutProps2 = {};
|
|
13424
|
-
if (!skipLayoutHooks && notFoundPage.layoutServerHooks && notFoundPage.layoutServerHooks.length > 0) {
|
|
13425
|
-
for (let i = 0; i < notFoundPage.layoutServerHooks.length; i++) {
|
|
13426
|
-
const layoutServerHook = notFoundPage.layoutServerHooks[i];
|
|
13427
|
-
const layoutMiddlewares = notFoundPage.layoutMiddlewares?.[i] || [];
|
|
13428
|
-
if (layoutMiddlewares.length > 0) {
|
|
13429
|
-
for (const mw of layoutMiddlewares) {
|
|
13430
|
-
try {
|
|
13431
|
-
await Promise.resolve(
|
|
13432
|
-
mw(ctx2, async () => {
|
|
13433
|
-
})
|
|
13434
|
-
);
|
|
13435
|
-
} catch (error) {
|
|
13436
|
-
const reqLogger2 = getRequestLogger(req);
|
|
13437
|
-
const layoutFile = notFoundPage.layoutFiles[i];
|
|
13438
|
-
const relativeLayoutPath = layoutFile ? import_path21.default.relative(projectRoot || process.cwd(), layoutFile) : "unknown";
|
|
13439
|
-
reqLogger2.error("Layout middleware failed for not-found page", error instanceof Error ? error : new Error(String(error)), {
|
|
13440
|
-
layoutIndex: i,
|
|
13441
|
-
layoutFile: relativeLayoutPath
|
|
13442
|
-
});
|
|
13443
|
-
throw error;
|
|
13444
|
-
}
|
|
13445
|
-
if (ctx2.res.headersSent) {
|
|
13446
|
-
return;
|
|
13447
|
-
}
|
|
13448
|
-
}
|
|
13449
|
-
}
|
|
13450
|
-
if (layoutServerHook) {
|
|
13451
|
-
try {
|
|
13452
|
-
const layoutResult = await layoutServerHook(ctx2);
|
|
13453
|
-
if (layoutResult.props) {
|
|
13454
|
-
Object.assign(layoutProps2, layoutResult.props);
|
|
13455
|
-
}
|
|
13456
|
-
} catch (error) {
|
|
13457
|
-
const reqLogger2 = getRequestLogger(req);
|
|
13458
|
-
const layoutFile = notFoundPage.layoutFiles[i];
|
|
13459
|
-
const relativeLayoutPath = layoutFile ? import_path21.default.relative(projectRoot || process.cwd(), layoutFile) : "unknown";
|
|
13460
|
-
reqLogger2.warn("Layout server hook failed for not-found page", {
|
|
13461
|
-
error: error instanceof Error ? error.message : String(error),
|
|
13462
|
-
stack: error instanceof Error ? error.stack : void 0,
|
|
13463
|
-
layoutFile: relativeLayoutPath,
|
|
13464
|
-
layoutIndex: i
|
|
13465
|
-
});
|
|
13466
|
-
}
|
|
13467
|
-
}
|
|
13468
|
-
}
|
|
13469
|
-
}
|
|
13470
|
-
let loaderResult2 = await runRouteServerHook(notFoundPage, ctx2);
|
|
13471
|
-
if (!loaderResult2.theme) {
|
|
13472
|
-
loaderResult2.theme = theme;
|
|
13473
|
-
}
|
|
13474
|
-
const combinedProps2 = {
|
|
13475
|
-
...layoutProps2,
|
|
13476
|
-
...loaderResult2.props || {}
|
|
13477
|
-
};
|
|
13478
|
-
const combinedLoaderResult2 = {
|
|
13479
|
-
...loaderResult2,
|
|
13480
|
-
props: combinedProps2
|
|
13481
|
-
};
|
|
13482
|
-
if (isDataReq) {
|
|
13483
|
-
const pagePropsOnly = loaderResult2.props || {};
|
|
13484
|
-
handleDataResponse(
|
|
13485
|
-
res,
|
|
13486
|
-
combinedLoaderResult2,
|
|
13487
|
-
theme,
|
|
13488
|
-
skipLayoutHooks ? null : Object.keys(layoutProps2).length > 0 ? layoutProps2 : null,
|
|
13489
|
-
pagePropsOnly
|
|
13490
|
-
);
|
|
13491
|
-
return;
|
|
13492
|
-
}
|
|
13493
|
-
const initialData2 = buildInitialData(finalUrlPath, {}, combinedLoaderResult2);
|
|
13494
|
-
const appTree2 = buildAppTree(notFoundPage, {}, initialData2.props);
|
|
13495
|
-
initialData2.notFound = true;
|
|
13496
|
-
const nonce2 = res.locals.nonce || void 0;
|
|
13497
|
-
const entrypointFiles2 = [];
|
|
13498
|
-
if (assetManifest?.entrypoints?.client) {
|
|
13499
|
-
entrypointFiles2.push(...assetManifest.entrypoints.client.map((file) => `${STATIC_PATH}/${file}`));
|
|
13500
|
-
}
|
|
13501
|
-
const documentTree2 = createDocumentTree({
|
|
13502
|
-
appTree: appTree2,
|
|
13503
|
-
initialData: initialData2,
|
|
13561
|
+
urlPath,
|
|
13562
|
+
finalUrlPath,
|
|
13504
13563
|
routerData,
|
|
13505
|
-
|
|
13506
|
-
titleFallback: "Not found",
|
|
13507
|
-
descriptionFallback: "Loly demo",
|
|
13508
|
-
chunkHref: null,
|
|
13509
|
-
entrypointFiles: entrypointFiles2,
|
|
13564
|
+
assetManifest,
|
|
13510
13565
|
theme,
|
|
13511
13566
|
clientJsPath,
|
|
13512
13567
|
clientCssPath,
|
|
13513
|
-
|
|
13514
|
-
|
|
13515
|
-
|
|
13516
|
-
|
|
13517
|
-
|
|
13518
|
-
|
|
13519
|
-
|
|
13520
|
-
|
|
13521
|
-
res.statusCode = 404;
|
|
13522
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
13523
|
-
pipe2(res);
|
|
13524
|
-
},
|
|
13525
|
-
onShellError(err) {
|
|
13526
|
-
didError2 = true;
|
|
13527
|
-
const reqLogger2 = getRequestLogger(req);
|
|
13528
|
-
reqLogger2.error("SSR shell error", err, { route: "not-found" });
|
|
13529
|
-
if (!res.headersSent && errorPage) {
|
|
13530
|
-
renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env, config);
|
|
13531
|
-
} else if (!res.headersSent) {
|
|
13532
|
-
res.statusCode = 500;
|
|
13533
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
13534
|
-
res.end("<!doctype html><h1>Internal Server Error</h1>");
|
|
13535
|
-
}
|
|
13536
|
-
abort2();
|
|
13537
|
-
},
|
|
13538
|
-
onError(err) {
|
|
13539
|
-
didError2 = true;
|
|
13540
|
-
const reqLogger2 = getRequestLogger(req);
|
|
13541
|
-
reqLogger2.error("SSR error", err, { route: "not-found" });
|
|
13542
|
-
}
|
|
13543
|
-
});
|
|
13544
|
-
req.on("close", () => abort2());
|
|
13568
|
+
faviconInfo,
|
|
13569
|
+
projectRoot,
|
|
13570
|
+
skipLayoutHooks,
|
|
13571
|
+
errorPage,
|
|
13572
|
+
routeChunks,
|
|
13573
|
+
env,
|
|
13574
|
+
config
|
|
13575
|
+
);
|
|
13545
13576
|
return;
|
|
13546
13577
|
}
|
|
13547
13578
|
handleNotFound(res, urlPath);
|
|
@@ -13554,7 +13585,9 @@ async function handlePageRequestInternal(options) {
|
|
|
13554
13585
|
res,
|
|
13555
13586
|
params: sanitizedParams,
|
|
13556
13587
|
pathname: urlPath,
|
|
13557
|
-
locals: {}
|
|
13588
|
+
locals: {},
|
|
13589
|
+
Redirect: (destination, permanent = false) => new RedirectResponse(destination, permanent),
|
|
13590
|
+
NotFound: () => new NotFoundResponse()
|
|
13558
13591
|
};
|
|
13559
13592
|
await runRouteMiddlewares(route, ctx);
|
|
13560
13593
|
if (res.headersSent) {
|
|
@@ -13592,6 +13625,48 @@ async function handlePageRequestInternal(options) {
|
|
|
13592
13625
|
if (layoutServerHook) {
|
|
13593
13626
|
try {
|
|
13594
13627
|
const layoutResult = await layoutServerHook(ctx);
|
|
13628
|
+
if (layoutResult instanceof RedirectResponse) {
|
|
13629
|
+
if (isDataReq) {
|
|
13630
|
+
res.statusCode = 200;
|
|
13631
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
13632
|
+
res.end(JSON.stringify({ redirect: { destination: layoutResult.destination, permanent: layoutResult.permanent } }));
|
|
13633
|
+
} else {
|
|
13634
|
+
handleRedirect(res, { destination: layoutResult.destination, permanent: layoutResult.permanent });
|
|
13635
|
+
}
|
|
13636
|
+
return;
|
|
13637
|
+
}
|
|
13638
|
+
if (layoutResult instanceof NotFoundResponse) {
|
|
13639
|
+
if (isDataReq) {
|
|
13640
|
+
res.statusCode = 200;
|
|
13641
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
13642
|
+
res.end(JSON.stringify({ notFound: true }));
|
|
13643
|
+
} else {
|
|
13644
|
+
if (notFoundPage) {
|
|
13645
|
+
await renderNotFoundPage(
|
|
13646
|
+
notFoundPage,
|
|
13647
|
+
req,
|
|
13648
|
+
res,
|
|
13649
|
+
urlPath,
|
|
13650
|
+
finalUrlPath,
|
|
13651
|
+
routerData,
|
|
13652
|
+
assetManifest,
|
|
13653
|
+
theme,
|
|
13654
|
+
clientJsPath,
|
|
13655
|
+
clientCssPath,
|
|
13656
|
+
faviconInfo,
|
|
13657
|
+
projectRoot,
|
|
13658
|
+
skipLayoutHooks,
|
|
13659
|
+
errorPage,
|
|
13660
|
+
routeChunks,
|
|
13661
|
+
env,
|
|
13662
|
+
config
|
|
13663
|
+
);
|
|
13664
|
+
} else {
|
|
13665
|
+
handleNotFound(res, urlPath);
|
|
13666
|
+
}
|
|
13667
|
+
}
|
|
13668
|
+
return;
|
|
13669
|
+
}
|
|
13595
13670
|
if (layoutResult.props) {
|
|
13596
13671
|
Object.assign(layoutProps, layoutResult.props);
|
|
13597
13672
|
}
|
|
@@ -13616,8 +13691,51 @@ async function handlePageRequestInternal(options) {
|
|
|
13616
13691
|
let loaderResult;
|
|
13617
13692
|
try {
|
|
13618
13693
|
loaderResult = await runRouteServerHook(route, ctx);
|
|
13619
|
-
if (
|
|
13620
|
-
|
|
13694
|
+
if (loaderResult instanceof RedirectResponse) {
|
|
13695
|
+
if (isDataReq) {
|
|
13696
|
+
res.statusCode = 200;
|
|
13697
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
13698
|
+
res.end(JSON.stringify({ redirect: { destination: loaderResult.destination, permanent: loaderResult.permanent } }));
|
|
13699
|
+
} else {
|
|
13700
|
+
handleRedirect(res, { destination: loaderResult.destination, permanent: loaderResult.permanent });
|
|
13701
|
+
}
|
|
13702
|
+
return;
|
|
13703
|
+
}
|
|
13704
|
+
if (loaderResult instanceof NotFoundResponse) {
|
|
13705
|
+
if (isDataReq) {
|
|
13706
|
+
res.statusCode = 200;
|
|
13707
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
13708
|
+
res.end(JSON.stringify({ notFound: true }));
|
|
13709
|
+
} else {
|
|
13710
|
+
if (notFoundPage) {
|
|
13711
|
+
await renderNotFoundPage(
|
|
13712
|
+
notFoundPage,
|
|
13713
|
+
req,
|
|
13714
|
+
res,
|
|
13715
|
+
urlPath,
|
|
13716
|
+
finalUrlPath,
|
|
13717
|
+
routerData,
|
|
13718
|
+
assetManifest,
|
|
13719
|
+
theme,
|
|
13720
|
+
clientJsPath,
|
|
13721
|
+
clientCssPath,
|
|
13722
|
+
faviconInfo,
|
|
13723
|
+
projectRoot,
|
|
13724
|
+
skipLayoutHooks,
|
|
13725
|
+
errorPage,
|
|
13726
|
+
routeChunks,
|
|
13727
|
+
env,
|
|
13728
|
+
config
|
|
13729
|
+
);
|
|
13730
|
+
} else {
|
|
13731
|
+
handleNotFound(res, urlPath);
|
|
13732
|
+
}
|
|
13733
|
+
}
|
|
13734
|
+
return;
|
|
13735
|
+
}
|
|
13736
|
+
const pageLoaderResult2 = loaderResult;
|
|
13737
|
+
if (!pageLoaderResult2.theme) {
|
|
13738
|
+
pageLoaderResult2.theme = theme;
|
|
13621
13739
|
}
|
|
13622
13740
|
} catch (error) {
|
|
13623
13741
|
const relativePagePath = route.pageFile ? import_path21.default.relative(projectRoot || process.cwd(), route.pageFile) : "unknown";
|
|
@@ -13642,17 +13760,18 @@ async function handlePageRequestInternal(options) {
|
|
|
13642
13760
|
return;
|
|
13643
13761
|
} else {
|
|
13644
13762
|
if (errorPage) {
|
|
13645
|
-
await renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env, config);
|
|
13763
|
+
await renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env, config, notFoundPage);
|
|
13646
13764
|
return;
|
|
13647
13765
|
} else {
|
|
13648
13766
|
throw error;
|
|
13649
13767
|
}
|
|
13650
13768
|
}
|
|
13651
13769
|
}
|
|
13770
|
+
const pageLoaderResult = loaderResult;
|
|
13652
13771
|
const combinedProps = {
|
|
13653
13772
|
...layoutProps,
|
|
13654
13773
|
// Props from layouts (stable)
|
|
13655
|
-
...
|
|
13774
|
+
...pageLoaderResult.props || {}
|
|
13656
13775
|
// Props from page (overrides layout)
|
|
13657
13776
|
};
|
|
13658
13777
|
let combinedMetadata = null;
|
|
@@ -13661,18 +13780,18 @@ async function handlePageRequestInternal(options) {
|
|
|
13661
13780
|
combinedMetadata = mergeMetadata(combinedMetadata, layoutMeta);
|
|
13662
13781
|
}
|
|
13663
13782
|
}
|
|
13664
|
-
if (
|
|
13665
|
-
combinedMetadata = mergeMetadata(combinedMetadata,
|
|
13783
|
+
if (pageLoaderResult.metadata) {
|
|
13784
|
+
combinedMetadata = mergeMetadata(combinedMetadata, pageLoaderResult.metadata);
|
|
13666
13785
|
}
|
|
13667
13786
|
const combinedLoaderResult = {
|
|
13668
|
-
...
|
|
13787
|
+
...pageLoaderResult,
|
|
13669
13788
|
props: combinedProps,
|
|
13670
13789
|
metadata: combinedMetadata,
|
|
13671
13790
|
pathname: finalUrlPath
|
|
13672
13791
|
// Include rewritten pathname for client-side matching
|
|
13673
13792
|
};
|
|
13674
13793
|
if (isDataReq) {
|
|
13675
|
-
const pagePropsOnly =
|
|
13794
|
+
const pagePropsOnly = pageLoaderResult.props || {};
|
|
13676
13795
|
handleDataResponse(
|
|
13677
13796
|
res,
|
|
13678
13797
|
combinedLoaderResult,
|
|
@@ -13682,18 +13801,6 @@ async function handlePageRequestInternal(options) {
|
|
|
13682
13801
|
);
|
|
13683
13802
|
return;
|
|
13684
13803
|
}
|
|
13685
|
-
if (loaderResult.redirect) {
|
|
13686
|
-
handleRedirect(res, loaderResult.redirect);
|
|
13687
|
-
return;
|
|
13688
|
-
}
|
|
13689
|
-
if (loaderResult.notFound) {
|
|
13690
|
-
if (isDataReq) {
|
|
13691
|
-
res.status(200).json({ notFound: true });
|
|
13692
|
-
} else {
|
|
13693
|
-
handleNotFound(res, urlPath);
|
|
13694
|
-
}
|
|
13695
|
-
return;
|
|
13696
|
-
}
|
|
13697
13804
|
const initialData = buildInitialData(finalUrlPath, params, combinedLoaderResult);
|
|
13698
13805
|
const appTree = buildAppTree(route, params, initialData.props);
|
|
13699
13806
|
const chunkName = routeChunks[route.pattern];
|
|
@@ -13750,7 +13857,7 @@ async function handlePageRequestInternal(options) {
|
|
|
13750
13857
|
}
|
|
13751
13858
|
console.error("\u{1F4A1} This usually indicates a React rendering error\n");
|
|
13752
13859
|
if (!res.headersSent && errorPage) {
|
|
13753
|
-
renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env);
|
|
13860
|
+
renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env, config, notFoundPage);
|
|
13754
13861
|
} else if (!res.headersSent) {
|
|
13755
13862
|
res.statusCode = 500;
|
|
13756
13863
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
@@ -13772,7 +13879,7 @@ async function handlePageRequestInternal(options) {
|
|
|
13772
13879
|
abort();
|
|
13773
13880
|
});
|
|
13774
13881
|
}
|
|
13775
|
-
async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env = "dev", config) {
|
|
13882
|
+
async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env = "dev", config, notFoundPage) {
|
|
13776
13883
|
try {
|
|
13777
13884
|
const isDataReq = isDataRequest(req);
|
|
13778
13885
|
const skipLayoutHooks = isDataReq && req.headers["x-skip-layout-hooks"] === "true";
|
|
@@ -13781,7 +13888,9 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
|
|
|
13781
13888
|
res,
|
|
13782
13889
|
params: { error: String(error) },
|
|
13783
13890
|
pathname: req.path,
|
|
13784
|
-
locals: { error }
|
|
13891
|
+
locals: { error },
|
|
13892
|
+
Redirect: (destination, permanent = false) => new RedirectResponse(destination, permanent),
|
|
13893
|
+
NotFound: () => new NotFoundResponse()
|
|
13785
13894
|
};
|
|
13786
13895
|
const faviconInfo = projectRoot && config ? getFaviconInfo(projectRoot, config.directories.static, env === "dev") : null;
|
|
13787
13896
|
const layoutProps = {};
|
|
@@ -13814,6 +13923,26 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
|
|
|
13814
13923
|
if (layoutServerHook) {
|
|
13815
13924
|
try {
|
|
13816
13925
|
const layoutResult = await layoutServerHook(ctx);
|
|
13926
|
+
if (layoutResult instanceof RedirectResponse) {
|
|
13927
|
+
if (isDataReq) {
|
|
13928
|
+
res.statusCode = 200;
|
|
13929
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
13930
|
+
res.end(JSON.stringify({ redirect: { destination: layoutResult.destination, permanent: layoutResult.permanent } }));
|
|
13931
|
+
} else {
|
|
13932
|
+
handleRedirect(res, { destination: layoutResult.destination, permanent: layoutResult.permanent });
|
|
13933
|
+
}
|
|
13934
|
+
return;
|
|
13935
|
+
}
|
|
13936
|
+
if (layoutResult instanceof NotFoundResponse) {
|
|
13937
|
+
if (isDataReq) {
|
|
13938
|
+
res.statusCode = 200;
|
|
13939
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
13940
|
+
res.end(JSON.stringify({ notFound: true }));
|
|
13941
|
+
} else {
|
|
13942
|
+
handleNotFound(res, req.path);
|
|
13943
|
+
}
|
|
13944
|
+
return;
|
|
13945
|
+
}
|
|
13817
13946
|
if (layoutResult.props) {
|
|
13818
13947
|
Object.assign(layoutProps, layoutResult.props);
|
|
13819
13948
|
}
|
|
@@ -13831,22 +13960,69 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
|
|
|
13831
13960
|
}
|
|
13832
13961
|
}
|
|
13833
13962
|
let loaderResult = await runRouteServerHook(errorPage, ctx);
|
|
13834
|
-
if (
|
|
13835
|
-
|
|
13963
|
+
if (loaderResult instanceof RedirectResponse) {
|
|
13964
|
+
if (isDataReq) {
|
|
13965
|
+
res.statusCode = 200;
|
|
13966
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
13967
|
+
res.end(JSON.stringify({ redirect: { destination: loaderResult.destination, permanent: loaderResult.permanent } }));
|
|
13968
|
+
} else {
|
|
13969
|
+
handleRedirect(res, { destination: loaderResult.destination, permanent: loaderResult.permanent });
|
|
13970
|
+
}
|
|
13971
|
+
return;
|
|
13972
|
+
}
|
|
13973
|
+
if (loaderResult instanceof NotFoundResponse) {
|
|
13974
|
+
if (isDataReq) {
|
|
13975
|
+
res.statusCode = 200;
|
|
13976
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
13977
|
+
res.end(JSON.stringify({ notFound: true }));
|
|
13978
|
+
} else {
|
|
13979
|
+
if (notFoundPage) {
|
|
13980
|
+
const notFoundAssetManifest = env === "prod" && projectRoot ? loadAssetManifest(projectRoot) : null;
|
|
13981
|
+
const notFoundClientJsPath = env === "dev" ? "/static/client.js" : projectRoot ? getClientJsPath(projectRoot) : "/static/client.js";
|
|
13982
|
+
const notFoundClientCssPath = env === "dev" ? "/static/client.css" : projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
|
|
13983
|
+
const notFoundFaviconInfo = projectRoot && config ? getFaviconInfo(projectRoot, config.directories.static, env === "dev") : null;
|
|
13984
|
+
await renderNotFoundPage(
|
|
13985
|
+
notFoundPage,
|
|
13986
|
+
req,
|
|
13987
|
+
res,
|
|
13988
|
+
req.path,
|
|
13989
|
+
req.path,
|
|
13990
|
+
buildRouterData(req),
|
|
13991
|
+
notFoundAssetManifest,
|
|
13992
|
+
theme,
|
|
13993
|
+
notFoundClientJsPath,
|
|
13994
|
+
notFoundClientCssPath,
|
|
13995
|
+
notFoundFaviconInfo,
|
|
13996
|
+
projectRoot,
|
|
13997
|
+
false,
|
|
13998
|
+
errorPage,
|
|
13999
|
+
routeChunks,
|
|
14000
|
+
env,
|
|
14001
|
+
config
|
|
14002
|
+
);
|
|
14003
|
+
} else {
|
|
14004
|
+
handleNotFound(res, req.path);
|
|
14005
|
+
}
|
|
14006
|
+
}
|
|
14007
|
+
return;
|
|
14008
|
+
}
|
|
14009
|
+
const errorLoaderResult = loaderResult;
|
|
14010
|
+
if (!errorLoaderResult.theme && theme) {
|
|
14011
|
+
errorLoaderResult.theme = theme;
|
|
13836
14012
|
}
|
|
13837
14013
|
const combinedProps = {
|
|
13838
14014
|
...layoutProps,
|
|
13839
|
-
...
|
|
14015
|
+
...errorLoaderResult.props || {}
|
|
13840
14016
|
};
|
|
13841
14017
|
const combinedLoaderResult = {
|
|
13842
|
-
...
|
|
14018
|
+
...errorLoaderResult,
|
|
13843
14019
|
props: combinedProps
|
|
13844
14020
|
};
|
|
13845
14021
|
const initialData = buildInitialData(req.path, { error: String(error) }, combinedLoaderResult);
|
|
13846
14022
|
const routerData = buildRouterData(req);
|
|
13847
14023
|
initialData.error = true;
|
|
13848
14024
|
if (isDataReq) {
|
|
13849
|
-
const pagePropsOnly =
|
|
14025
|
+
const pagePropsOnly = errorLoaderResult.props || {};
|
|
13850
14026
|
handleDataResponse(
|
|
13851
14027
|
res,
|
|
13852
14028
|
combinedLoaderResult,
|
|
@@ -13975,7 +14151,9 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params,
|
|
|
13975
14151
|
res,
|
|
13976
14152
|
params,
|
|
13977
14153
|
pathname: urlPath,
|
|
13978
|
-
locals: {}
|
|
14154
|
+
locals: {},
|
|
14155
|
+
Redirect: (destination, permanent = false) => new RedirectResponse(destination, permanent),
|
|
14156
|
+
NotFound: () => new NotFoundResponse()
|
|
13979
14157
|
};
|
|
13980
14158
|
for (const mw of route.middlewares) {
|
|
13981
14159
|
await Promise.resolve(
|
|
@@ -13991,6 +14169,12 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params,
|
|
|
13991
14169
|
if (layoutServerHook) {
|
|
13992
14170
|
try {
|
|
13993
14171
|
const layoutResult = await layoutServerHook(ctx);
|
|
14172
|
+
if (layoutResult instanceof RedirectResponse || layoutResult instanceof NotFoundResponse) {
|
|
14173
|
+
console.warn(
|
|
14174
|
+
`\u26A0\uFE0F [framework][ssg] Layout server hook ${i} returned redirect/notFound for route ${route.pattern}. Redirect/NotFound is not supported in SSG (Static Site Generation). Skipping this route.`
|
|
14175
|
+
);
|
|
14176
|
+
return;
|
|
14177
|
+
}
|
|
13994
14178
|
if (layoutResult.props) {
|
|
13995
14179
|
Object.assign(layoutProps, layoutResult.props);
|
|
13996
14180
|
}
|
|
@@ -14012,10 +14196,17 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params,
|
|
|
14012
14196
|
let loaderResult = { props: {} };
|
|
14013
14197
|
if (route.loader) {
|
|
14014
14198
|
loaderResult = await route.loader(ctx);
|
|
14199
|
+
if (loaderResult instanceof RedirectResponse || loaderResult instanceof NotFoundResponse) {
|
|
14200
|
+
console.warn(
|
|
14201
|
+
`\u26A0\uFE0F [framework][ssg] Page server hook returned redirect/notFound for route ${route.pattern} (${urlPath}). Redirect/NotFound is not supported in SSG (Static Site Generation). Skipping this route.`
|
|
14202
|
+
);
|
|
14203
|
+
return;
|
|
14204
|
+
}
|
|
14015
14205
|
}
|
|
14206
|
+
const pageLoaderResult = loaderResult;
|
|
14016
14207
|
const combinedProps = {
|
|
14017
14208
|
...layoutProps,
|
|
14018
|
-
...
|
|
14209
|
+
...pageLoaderResult.props || {}
|
|
14019
14210
|
};
|
|
14020
14211
|
let combinedMetadata = null;
|
|
14021
14212
|
for (const layoutMeta of layoutMetadata) {
|
|
@@ -14023,17 +14214,14 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params,
|
|
|
14023
14214
|
combinedMetadata = mergeMetadata(combinedMetadata, layoutMeta);
|
|
14024
14215
|
}
|
|
14025
14216
|
}
|
|
14026
|
-
if (
|
|
14027
|
-
combinedMetadata = mergeMetadata(combinedMetadata,
|
|
14217
|
+
if (pageLoaderResult.metadata) {
|
|
14218
|
+
combinedMetadata = mergeMetadata(combinedMetadata, pageLoaderResult.metadata);
|
|
14028
14219
|
}
|
|
14029
14220
|
const combinedLoaderResult = {
|
|
14030
|
-
...
|
|
14221
|
+
...pageLoaderResult,
|
|
14031
14222
|
props: combinedProps,
|
|
14032
14223
|
metadata: combinedMetadata
|
|
14033
14224
|
};
|
|
14034
|
-
if (combinedLoaderResult.redirect || combinedLoaderResult.notFound) {
|
|
14035
|
-
return;
|
|
14036
|
-
}
|
|
14037
14225
|
const initialData = buildInitialData(urlPath, params, combinedLoaderResult);
|
|
14038
14226
|
const routerData = buildRouterData(req);
|
|
14039
14227
|
const appTree = buildAppTree(route, params, initialData.props);
|