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