@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/index.js
CHANGED
|
@@ -10782,6 +10782,18 @@ function writeRoutesManifest({
|
|
|
10782
10782
|
fs7.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2), "utf-8");
|
|
10783
10783
|
}
|
|
10784
10784
|
|
|
10785
|
+
// modules/router/index.types.ts
|
|
10786
|
+
var RedirectResponse = class {
|
|
10787
|
+
constructor(destination, permanent = false) {
|
|
10788
|
+
this.destination = destination;
|
|
10789
|
+
this.permanent = permanent;
|
|
10790
|
+
}
|
|
10791
|
+
};
|
|
10792
|
+
var NotFoundResponse = class {
|
|
10793
|
+
constructor() {
|
|
10794
|
+
}
|
|
10795
|
+
};
|
|
10796
|
+
|
|
10785
10797
|
// modules/router/loader-routes.ts
|
|
10786
10798
|
import fs8 from "fs";
|
|
10787
10799
|
import path8 from "path";
|
|
@@ -15605,16 +15617,6 @@ async function runRouteServerHook(route, ctx) {
|
|
|
15605
15617
|
// modules/server/handlers/response.ts
|
|
15606
15618
|
function handleDataResponse(res, loaderResult, theme, layoutProps, pageProps, error, message) {
|
|
15607
15619
|
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
15608
|
-
if (loaderResult.redirect) {
|
|
15609
|
-
res.statusCode = 200;
|
|
15610
|
-
res.end(JSON.stringify({ redirect: loaderResult.redirect }));
|
|
15611
|
-
return;
|
|
15612
|
-
}
|
|
15613
|
-
if (loaderResult.notFound) {
|
|
15614
|
-
res.statusCode = 404;
|
|
15615
|
-
res.end(JSON.stringify({ notFound: true }));
|
|
15616
|
-
return;
|
|
15617
|
-
}
|
|
15618
15620
|
const response = {
|
|
15619
15621
|
// Combined props for backward compatibility
|
|
15620
15622
|
props: loaderResult.props ?? {},
|
|
@@ -15735,6 +15737,145 @@ function mergeMetadata(base, override) {
|
|
|
15735
15737
|
function isDataRequest(req) {
|
|
15736
15738
|
return req.query && req.query.__fw_data === "1" || req.headers["x-fw-data"] === "1";
|
|
15737
15739
|
}
|
|
15740
|
+
async function renderNotFoundPage(notFoundPage, req, res, urlPath, finalUrlPath, routerData, assetManifest, theme, clientJsPath, clientCssPath, faviconInfo, projectRoot, skipLayoutHooks, errorPage, routeChunks, env, config) {
|
|
15741
|
+
const ctx = {
|
|
15742
|
+
req,
|
|
15743
|
+
res,
|
|
15744
|
+
params: {},
|
|
15745
|
+
pathname: urlPath,
|
|
15746
|
+
locals: {},
|
|
15747
|
+
Redirect: (destination, permanent = false) => new RedirectResponse(destination, permanent),
|
|
15748
|
+
NotFound: () => new NotFoundResponse()
|
|
15749
|
+
};
|
|
15750
|
+
const layoutProps = {};
|
|
15751
|
+
if (!skipLayoutHooks && notFoundPage.layoutServerHooks && notFoundPage.layoutServerHooks.length > 0) {
|
|
15752
|
+
for (let i = 0; i < notFoundPage.layoutServerHooks.length; i++) {
|
|
15753
|
+
const layoutServerHook = notFoundPage.layoutServerHooks[i];
|
|
15754
|
+
const layoutMiddlewares = notFoundPage.layoutMiddlewares?.[i] || [];
|
|
15755
|
+
if (layoutMiddlewares.length > 0) {
|
|
15756
|
+
for (const mw of layoutMiddlewares) {
|
|
15757
|
+
try {
|
|
15758
|
+
await Promise.resolve(
|
|
15759
|
+
mw(ctx, async () => {
|
|
15760
|
+
})
|
|
15761
|
+
);
|
|
15762
|
+
} catch (error) {
|
|
15763
|
+
const reqLogger = getRequestLogger(req);
|
|
15764
|
+
const layoutFile = notFoundPage.layoutFiles[i];
|
|
15765
|
+
const relativeLayoutPath = layoutFile ? path23.relative(projectRoot || process.cwd(), layoutFile) : "unknown";
|
|
15766
|
+
reqLogger.error("Layout middleware failed for not-found page", error instanceof Error ? error : new Error(String(error)), {
|
|
15767
|
+
layoutIndex: i,
|
|
15768
|
+
layoutFile: relativeLayoutPath
|
|
15769
|
+
});
|
|
15770
|
+
throw error;
|
|
15771
|
+
}
|
|
15772
|
+
if (ctx.res.headersSent) {
|
|
15773
|
+
return;
|
|
15774
|
+
}
|
|
15775
|
+
}
|
|
15776
|
+
}
|
|
15777
|
+
if (layoutServerHook) {
|
|
15778
|
+
try {
|
|
15779
|
+
const layoutResult = await layoutServerHook(ctx);
|
|
15780
|
+
if (layoutResult instanceof RedirectResponse) {
|
|
15781
|
+
handleRedirect(res, { destination: layoutResult.destination, permanent: layoutResult.permanent });
|
|
15782
|
+
return;
|
|
15783
|
+
}
|
|
15784
|
+
if (layoutResult instanceof NotFoundResponse) {
|
|
15785
|
+
handleNotFound(res, urlPath);
|
|
15786
|
+
return;
|
|
15787
|
+
}
|
|
15788
|
+
if (layoutResult.props) {
|
|
15789
|
+
Object.assign(layoutProps, layoutResult.props);
|
|
15790
|
+
}
|
|
15791
|
+
} catch (error) {
|
|
15792
|
+
const reqLogger = getRequestLogger(req);
|
|
15793
|
+
const layoutFile = notFoundPage.layoutFiles[i];
|
|
15794
|
+
const relativeLayoutPath = layoutFile ? path23.relative(projectRoot || process.cwd(), layoutFile) : "unknown";
|
|
15795
|
+
reqLogger.warn("Layout server hook failed for not-found page", {
|
|
15796
|
+
error: error instanceof Error ? error.message : String(error),
|
|
15797
|
+
stack: error instanceof Error ? error.stack : void 0,
|
|
15798
|
+
layoutFile: relativeLayoutPath,
|
|
15799
|
+
layoutIndex: i
|
|
15800
|
+
});
|
|
15801
|
+
}
|
|
15802
|
+
}
|
|
15803
|
+
}
|
|
15804
|
+
}
|
|
15805
|
+
let loaderResult = await runRouteServerHook(notFoundPage, ctx);
|
|
15806
|
+
if (loaderResult instanceof RedirectResponse) {
|
|
15807
|
+
handleRedirect(res, { destination: loaderResult.destination, permanent: loaderResult.permanent });
|
|
15808
|
+
return;
|
|
15809
|
+
}
|
|
15810
|
+
if (loaderResult instanceof NotFoundResponse) {
|
|
15811
|
+
handleNotFound(res, urlPath);
|
|
15812
|
+
return;
|
|
15813
|
+
}
|
|
15814
|
+
const notFoundLoaderResult = loaderResult;
|
|
15815
|
+
if (!notFoundLoaderResult.theme) {
|
|
15816
|
+
notFoundLoaderResult.theme = theme;
|
|
15817
|
+
}
|
|
15818
|
+
const combinedProps = {
|
|
15819
|
+
...layoutProps,
|
|
15820
|
+
...notFoundLoaderResult.props || {}
|
|
15821
|
+
};
|
|
15822
|
+
const combinedLoaderResult = {
|
|
15823
|
+
...notFoundLoaderResult,
|
|
15824
|
+
props: combinedProps
|
|
15825
|
+
};
|
|
15826
|
+
const initialData = buildInitialData(finalUrlPath, {}, combinedLoaderResult);
|
|
15827
|
+
const appTree = buildAppTree(notFoundPage, {}, initialData.props);
|
|
15828
|
+
initialData.notFound = true;
|
|
15829
|
+
const nonce = res.locals.nonce || void 0;
|
|
15830
|
+
const entrypointFiles = [];
|
|
15831
|
+
if (assetManifest?.entrypoints?.client) {
|
|
15832
|
+
entrypointFiles.push(...assetManifest.entrypoints.client.map((file) => `${STATIC_PATH}/${file}`));
|
|
15833
|
+
}
|
|
15834
|
+
const documentTree = createDocumentTree({
|
|
15835
|
+
appTree,
|
|
15836
|
+
initialData,
|
|
15837
|
+
routerData,
|
|
15838
|
+
meta: combinedLoaderResult.metadata ?? null,
|
|
15839
|
+
titleFallback: "Not found",
|
|
15840
|
+
descriptionFallback: "Loly demo",
|
|
15841
|
+
chunkHref: null,
|
|
15842
|
+
entrypointFiles,
|
|
15843
|
+
theme,
|
|
15844
|
+
clientJsPath,
|
|
15845
|
+
clientCssPath,
|
|
15846
|
+
nonce,
|
|
15847
|
+
faviconPath: faviconInfo?.path || null,
|
|
15848
|
+
faviconType: faviconInfo?.type || null
|
|
15849
|
+
});
|
|
15850
|
+
let didError = false;
|
|
15851
|
+
const { pipe, abort } = renderToPipeableStream(documentTree, {
|
|
15852
|
+
onShellReady() {
|
|
15853
|
+
if (didError || res.headersSent) return;
|
|
15854
|
+
res.statusCode = 404;
|
|
15855
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
15856
|
+
pipe(res);
|
|
15857
|
+
},
|
|
15858
|
+
onShellError(err) {
|
|
15859
|
+
didError = true;
|
|
15860
|
+
const reqLogger = getRequestLogger(req);
|
|
15861
|
+
reqLogger.error("SSR shell error", err, { route: "not-found" });
|
|
15862
|
+
if (!res.headersSent && errorPage) {
|
|
15863
|
+
renderErrorPageWithStream(errorPage, req, res, err, routeChunks || {}, theme, projectRoot, env, config, notFoundPage);
|
|
15864
|
+
} else if (!res.headersSent) {
|
|
15865
|
+
res.statusCode = 500;
|
|
15866
|
+
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
15867
|
+
res.end("<!doctype html><h1>Internal Server Error</h1>");
|
|
15868
|
+
}
|
|
15869
|
+
abort();
|
|
15870
|
+
},
|
|
15871
|
+
onError(err) {
|
|
15872
|
+
didError = true;
|
|
15873
|
+
const reqLogger = getRequestLogger(req);
|
|
15874
|
+
reqLogger.error("SSR error", err, { route: "not-found" });
|
|
15875
|
+
}
|
|
15876
|
+
});
|
|
15877
|
+
req.on("close", () => abort());
|
|
15878
|
+
}
|
|
15738
15879
|
async function handlePageRequest(options) {
|
|
15739
15880
|
try {
|
|
15740
15881
|
await handlePageRequestInternal(options);
|
|
@@ -15742,7 +15883,7 @@ async function handlePageRequest(options) {
|
|
|
15742
15883
|
const { errorPage, req, res, routeChunks, theme, projectRoot } = options;
|
|
15743
15884
|
const reqLogger = getRequestLogger(req);
|
|
15744
15885
|
if (errorPage) {
|
|
15745
|
-
await renderErrorPageWithStream(errorPage, req, res, error, routeChunks || {}, theme, projectRoot, options.env, options.config);
|
|
15886
|
+
await renderErrorPageWithStream(errorPage, req, res, error, routeChunks || {}, theme, projectRoot, options.env, options.config, options.notFoundPage);
|
|
15746
15887
|
} else {
|
|
15747
15888
|
reqLogger.error("Unhandled error in page request", error, {
|
|
15748
15889
|
urlPath: options.urlPath,
|
|
@@ -15852,135 +15993,25 @@ async function handlePageRequestInternal(options) {
|
|
|
15852
15993
|
return;
|
|
15853
15994
|
}
|
|
15854
15995
|
if (notFoundPage) {
|
|
15855
|
-
|
|
15996
|
+
await renderNotFoundPage(
|
|
15997
|
+
notFoundPage,
|
|
15856
15998
|
req,
|
|
15857
15999
|
res,
|
|
15858
|
-
|
|
15859
|
-
|
|
15860
|
-
locals: {}
|
|
15861
|
-
};
|
|
15862
|
-
const layoutProps2 = {};
|
|
15863
|
-
if (!skipLayoutHooks && notFoundPage.layoutServerHooks && notFoundPage.layoutServerHooks.length > 0) {
|
|
15864
|
-
for (let i = 0; i < notFoundPage.layoutServerHooks.length; i++) {
|
|
15865
|
-
const layoutServerHook = notFoundPage.layoutServerHooks[i];
|
|
15866
|
-
const layoutMiddlewares = notFoundPage.layoutMiddlewares?.[i] || [];
|
|
15867
|
-
if (layoutMiddlewares.length > 0) {
|
|
15868
|
-
for (const mw of layoutMiddlewares) {
|
|
15869
|
-
try {
|
|
15870
|
-
await Promise.resolve(
|
|
15871
|
-
mw(ctx2, async () => {
|
|
15872
|
-
})
|
|
15873
|
-
);
|
|
15874
|
-
} catch (error) {
|
|
15875
|
-
const reqLogger2 = getRequestLogger(req);
|
|
15876
|
-
const layoutFile = notFoundPage.layoutFiles[i];
|
|
15877
|
-
const relativeLayoutPath = layoutFile ? path23.relative(projectRoot || process.cwd(), layoutFile) : "unknown";
|
|
15878
|
-
reqLogger2.error("Layout middleware failed for not-found page", error instanceof Error ? error : new Error(String(error)), {
|
|
15879
|
-
layoutIndex: i,
|
|
15880
|
-
layoutFile: relativeLayoutPath
|
|
15881
|
-
});
|
|
15882
|
-
throw error;
|
|
15883
|
-
}
|
|
15884
|
-
if (ctx2.res.headersSent) {
|
|
15885
|
-
return;
|
|
15886
|
-
}
|
|
15887
|
-
}
|
|
15888
|
-
}
|
|
15889
|
-
if (layoutServerHook) {
|
|
15890
|
-
try {
|
|
15891
|
-
const layoutResult = await layoutServerHook(ctx2);
|
|
15892
|
-
if (layoutResult.props) {
|
|
15893
|
-
Object.assign(layoutProps2, layoutResult.props);
|
|
15894
|
-
}
|
|
15895
|
-
} catch (error) {
|
|
15896
|
-
const reqLogger2 = getRequestLogger(req);
|
|
15897
|
-
const layoutFile = notFoundPage.layoutFiles[i];
|
|
15898
|
-
const relativeLayoutPath = layoutFile ? path23.relative(projectRoot || process.cwd(), layoutFile) : "unknown";
|
|
15899
|
-
reqLogger2.warn("Layout server hook failed for not-found page", {
|
|
15900
|
-
error: error instanceof Error ? error.message : String(error),
|
|
15901
|
-
stack: error instanceof Error ? error.stack : void 0,
|
|
15902
|
-
layoutFile: relativeLayoutPath,
|
|
15903
|
-
layoutIndex: i
|
|
15904
|
-
});
|
|
15905
|
-
}
|
|
15906
|
-
}
|
|
15907
|
-
}
|
|
15908
|
-
}
|
|
15909
|
-
let loaderResult2 = await runRouteServerHook(notFoundPage, ctx2);
|
|
15910
|
-
if (!loaderResult2.theme) {
|
|
15911
|
-
loaderResult2.theme = theme;
|
|
15912
|
-
}
|
|
15913
|
-
const combinedProps2 = {
|
|
15914
|
-
...layoutProps2,
|
|
15915
|
-
...loaderResult2.props || {}
|
|
15916
|
-
};
|
|
15917
|
-
const combinedLoaderResult2 = {
|
|
15918
|
-
...loaderResult2,
|
|
15919
|
-
props: combinedProps2
|
|
15920
|
-
};
|
|
15921
|
-
if (isDataReq) {
|
|
15922
|
-
const pagePropsOnly = loaderResult2.props || {};
|
|
15923
|
-
handleDataResponse(
|
|
15924
|
-
res,
|
|
15925
|
-
combinedLoaderResult2,
|
|
15926
|
-
theme,
|
|
15927
|
-
skipLayoutHooks ? null : Object.keys(layoutProps2).length > 0 ? layoutProps2 : null,
|
|
15928
|
-
pagePropsOnly
|
|
15929
|
-
);
|
|
15930
|
-
return;
|
|
15931
|
-
}
|
|
15932
|
-
const initialData2 = buildInitialData(finalUrlPath, {}, combinedLoaderResult2);
|
|
15933
|
-
const appTree2 = buildAppTree(notFoundPage, {}, initialData2.props);
|
|
15934
|
-
initialData2.notFound = true;
|
|
15935
|
-
const nonce2 = res.locals.nonce || void 0;
|
|
15936
|
-
const entrypointFiles2 = [];
|
|
15937
|
-
if (assetManifest?.entrypoints?.client) {
|
|
15938
|
-
entrypointFiles2.push(...assetManifest.entrypoints.client.map((file) => `${STATIC_PATH}/${file}`));
|
|
15939
|
-
}
|
|
15940
|
-
const documentTree2 = createDocumentTree({
|
|
15941
|
-
appTree: appTree2,
|
|
15942
|
-
initialData: initialData2,
|
|
16000
|
+
urlPath,
|
|
16001
|
+
finalUrlPath,
|
|
15943
16002
|
routerData,
|
|
15944
|
-
|
|
15945
|
-
titleFallback: "Not found",
|
|
15946
|
-
descriptionFallback: "Loly demo",
|
|
15947
|
-
chunkHref: null,
|
|
15948
|
-
entrypointFiles: entrypointFiles2,
|
|
16003
|
+
assetManifest,
|
|
15949
16004
|
theme,
|
|
15950
16005
|
clientJsPath,
|
|
15951
16006
|
clientCssPath,
|
|
15952
|
-
|
|
15953
|
-
|
|
15954
|
-
|
|
15955
|
-
|
|
15956
|
-
|
|
15957
|
-
|
|
15958
|
-
|
|
15959
|
-
|
|
15960
|
-
res.statusCode = 404;
|
|
15961
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
15962
|
-
pipe2(res);
|
|
15963
|
-
},
|
|
15964
|
-
onShellError(err) {
|
|
15965
|
-
didError2 = true;
|
|
15966
|
-
const reqLogger2 = getRequestLogger(req);
|
|
15967
|
-
reqLogger2.error("SSR shell error", err, { route: "not-found" });
|
|
15968
|
-
if (!res.headersSent && errorPage) {
|
|
15969
|
-
renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env, config);
|
|
15970
|
-
} else if (!res.headersSent) {
|
|
15971
|
-
res.statusCode = 500;
|
|
15972
|
-
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
15973
|
-
res.end("<!doctype html><h1>Internal Server Error</h1>");
|
|
15974
|
-
}
|
|
15975
|
-
abort2();
|
|
15976
|
-
},
|
|
15977
|
-
onError(err) {
|
|
15978
|
-
didError2 = true;
|
|
15979
|
-
const reqLogger2 = getRequestLogger(req);
|
|
15980
|
-
reqLogger2.error("SSR error", err, { route: "not-found" });
|
|
15981
|
-
}
|
|
15982
|
-
});
|
|
15983
|
-
req.on("close", () => abort2());
|
|
16007
|
+
faviconInfo,
|
|
16008
|
+
projectRoot,
|
|
16009
|
+
skipLayoutHooks,
|
|
16010
|
+
errorPage,
|
|
16011
|
+
routeChunks,
|
|
16012
|
+
env,
|
|
16013
|
+
config
|
|
16014
|
+
);
|
|
15984
16015
|
return;
|
|
15985
16016
|
}
|
|
15986
16017
|
handleNotFound(res, urlPath);
|
|
@@ -15993,7 +16024,9 @@ async function handlePageRequestInternal(options) {
|
|
|
15993
16024
|
res,
|
|
15994
16025
|
params: sanitizedParams,
|
|
15995
16026
|
pathname: urlPath,
|
|
15996
|
-
locals: {}
|
|
16027
|
+
locals: {},
|
|
16028
|
+
Redirect: (destination, permanent = false) => new RedirectResponse(destination, permanent),
|
|
16029
|
+
NotFound: () => new NotFoundResponse()
|
|
15997
16030
|
};
|
|
15998
16031
|
await runRouteMiddlewares(route, ctx);
|
|
15999
16032
|
if (res.headersSent) {
|
|
@@ -16031,6 +16064,48 @@ async function handlePageRequestInternal(options) {
|
|
|
16031
16064
|
if (layoutServerHook) {
|
|
16032
16065
|
try {
|
|
16033
16066
|
const layoutResult = await layoutServerHook(ctx);
|
|
16067
|
+
if (layoutResult instanceof RedirectResponse) {
|
|
16068
|
+
if (isDataReq) {
|
|
16069
|
+
res.statusCode = 200;
|
|
16070
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
16071
|
+
res.end(JSON.stringify({ redirect: { destination: layoutResult.destination, permanent: layoutResult.permanent } }));
|
|
16072
|
+
} else {
|
|
16073
|
+
handleRedirect(res, { destination: layoutResult.destination, permanent: layoutResult.permanent });
|
|
16074
|
+
}
|
|
16075
|
+
return;
|
|
16076
|
+
}
|
|
16077
|
+
if (layoutResult instanceof NotFoundResponse) {
|
|
16078
|
+
if (isDataReq) {
|
|
16079
|
+
res.statusCode = 200;
|
|
16080
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
16081
|
+
res.end(JSON.stringify({ notFound: true }));
|
|
16082
|
+
} else {
|
|
16083
|
+
if (notFoundPage) {
|
|
16084
|
+
await renderNotFoundPage(
|
|
16085
|
+
notFoundPage,
|
|
16086
|
+
req,
|
|
16087
|
+
res,
|
|
16088
|
+
urlPath,
|
|
16089
|
+
finalUrlPath,
|
|
16090
|
+
routerData,
|
|
16091
|
+
assetManifest,
|
|
16092
|
+
theme,
|
|
16093
|
+
clientJsPath,
|
|
16094
|
+
clientCssPath,
|
|
16095
|
+
faviconInfo,
|
|
16096
|
+
projectRoot,
|
|
16097
|
+
skipLayoutHooks,
|
|
16098
|
+
errorPage,
|
|
16099
|
+
routeChunks,
|
|
16100
|
+
env,
|
|
16101
|
+
config
|
|
16102
|
+
);
|
|
16103
|
+
} else {
|
|
16104
|
+
handleNotFound(res, urlPath);
|
|
16105
|
+
}
|
|
16106
|
+
}
|
|
16107
|
+
return;
|
|
16108
|
+
}
|
|
16034
16109
|
if (layoutResult.props) {
|
|
16035
16110
|
Object.assign(layoutProps, layoutResult.props);
|
|
16036
16111
|
}
|
|
@@ -16055,8 +16130,51 @@ async function handlePageRequestInternal(options) {
|
|
|
16055
16130
|
let loaderResult;
|
|
16056
16131
|
try {
|
|
16057
16132
|
loaderResult = await runRouteServerHook(route, ctx);
|
|
16058
|
-
if (
|
|
16059
|
-
|
|
16133
|
+
if (loaderResult instanceof RedirectResponse) {
|
|
16134
|
+
if (isDataReq) {
|
|
16135
|
+
res.statusCode = 200;
|
|
16136
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
16137
|
+
res.end(JSON.stringify({ redirect: { destination: loaderResult.destination, permanent: loaderResult.permanent } }));
|
|
16138
|
+
} else {
|
|
16139
|
+
handleRedirect(res, { destination: loaderResult.destination, permanent: loaderResult.permanent });
|
|
16140
|
+
}
|
|
16141
|
+
return;
|
|
16142
|
+
}
|
|
16143
|
+
if (loaderResult instanceof NotFoundResponse) {
|
|
16144
|
+
if (isDataReq) {
|
|
16145
|
+
res.statusCode = 200;
|
|
16146
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
16147
|
+
res.end(JSON.stringify({ notFound: true }));
|
|
16148
|
+
} else {
|
|
16149
|
+
if (notFoundPage) {
|
|
16150
|
+
await renderNotFoundPage(
|
|
16151
|
+
notFoundPage,
|
|
16152
|
+
req,
|
|
16153
|
+
res,
|
|
16154
|
+
urlPath,
|
|
16155
|
+
finalUrlPath,
|
|
16156
|
+
routerData,
|
|
16157
|
+
assetManifest,
|
|
16158
|
+
theme,
|
|
16159
|
+
clientJsPath,
|
|
16160
|
+
clientCssPath,
|
|
16161
|
+
faviconInfo,
|
|
16162
|
+
projectRoot,
|
|
16163
|
+
skipLayoutHooks,
|
|
16164
|
+
errorPage,
|
|
16165
|
+
routeChunks,
|
|
16166
|
+
env,
|
|
16167
|
+
config
|
|
16168
|
+
);
|
|
16169
|
+
} else {
|
|
16170
|
+
handleNotFound(res, urlPath);
|
|
16171
|
+
}
|
|
16172
|
+
}
|
|
16173
|
+
return;
|
|
16174
|
+
}
|
|
16175
|
+
const pageLoaderResult2 = loaderResult;
|
|
16176
|
+
if (!pageLoaderResult2.theme) {
|
|
16177
|
+
pageLoaderResult2.theme = theme;
|
|
16060
16178
|
}
|
|
16061
16179
|
} catch (error) {
|
|
16062
16180
|
const relativePagePath = route.pageFile ? path23.relative(projectRoot || process.cwd(), route.pageFile) : "unknown";
|
|
@@ -16081,17 +16199,18 @@ async function handlePageRequestInternal(options) {
|
|
|
16081
16199
|
return;
|
|
16082
16200
|
} else {
|
|
16083
16201
|
if (errorPage) {
|
|
16084
|
-
await renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env, config);
|
|
16202
|
+
await renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env, config, notFoundPage);
|
|
16085
16203
|
return;
|
|
16086
16204
|
} else {
|
|
16087
16205
|
throw error;
|
|
16088
16206
|
}
|
|
16089
16207
|
}
|
|
16090
16208
|
}
|
|
16209
|
+
const pageLoaderResult = loaderResult;
|
|
16091
16210
|
const combinedProps = {
|
|
16092
16211
|
...layoutProps,
|
|
16093
16212
|
// Props from layouts (stable)
|
|
16094
|
-
...
|
|
16213
|
+
...pageLoaderResult.props || {}
|
|
16095
16214
|
// Props from page (overrides layout)
|
|
16096
16215
|
};
|
|
16097
16216
|
let combinedMetadata = null;
|
|
@@ -16100,18 +16219,18 @@ async function handlePageRequestInternal(options) {
|
|
|
16100
16219
|
combinedMetadata = mergeMetadata(combinedMetadata, layoutMeta);
|
|
16101
16220
|
}
|
|
16102
16221
|
}
|
|
16103
|
-
if (
|
|
16104
|
-
combinedMetadata = mergeMetadata(combinedMetadata,
|
|
16222
|
+
if (pageLoaderResult.metadata) {
|
|
16223
|
+
combinedMetadata = mergeMetadata(combinedMetadata, pageLoaderResult.metadata);
|
|
16105
16224
|
}
|
|
16106
16225
|
const combinedLoaderResult = {
|
|
16107
|
-
...
|
|
16226
|
+
...pageLoaderResult,
|
|
16108
16227
|
props: combinedProps,
|
|
16109
16228
|
metadata: combinedMetadata,
|
|
16110
16229
|
pathname: finalUrlPath
|
|
16111
16230
|
// Include rewritten pathname for client-side matching
|
|
16112
16231
|
};
|
|
16113
16232
|
if (isDataReq) {
|
|
16114
|
-
const pagePropsOnly =
|
|
16233
|
+
const pagePropsOnly = pageLoaderResult.props || {};
|
|
16115
16234
|
handleDataResponse(
|
|
16116
16235
|
res,
|
|
16117
16236
|
combinedLoaderResult,
|
|
@@ -16121,18 +16240,6 @@ async function handlePageRequestInternal(options) {
|
|
|
16121
16240
|
);
|
|
16122
16241
|
return;
|
|
16123
16242
|
}
|
|
16124
|
-
if (loaderResult.redirect) {
|
|
16125
|
-
handleRedirect(res, loaderResult.redirect);
|
|
16126
|
-
return;
|
|
16127
|
-
}
|
|
16128
|
-
if (loaderResult.notFound) {
|
|
16129
|
-
if (isDataReq) {
|
|
16130
|
-
res.status(200).json({ notFound: true });
|
|
16131
|
-
} else {
|
|
16132
|
-
handleNotFound(res, urlPath);
|
|
16133
|
-
}
|
|
16134
|
-
return;
|
|
16135
|
-
}
|
|
16136
16243
|
const initialData = buildInitialData(finalUrlPath, params, combinedLoaderResult);
|
|
16137
16244
|
const appTree = buildAppTree(route, params, initialData.props);
|
|
16138
16245
|
const chunkName = routeChunks[route.pattern];
|
|
@@ -16189,7 +16296,7 @@ async function handlePageRequestInternal(options) {
|
|
|
16189
16296
|
}
|
|
16190
16297
|
console.error("\u{1F4A1} This usually indicates a React rendering error\n");
|
|
16191
16298
|
if (!res.headersSent && errorPage) {
|
|
16192
|
-
renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env);
|
|
16299
|
+
renderErrorPageWithStream(errorPage, req, res, err, routeChunks, theme, projectRoot, env, config, notFoundPage);
|
|
16193
16300
|
} else if (!res.headersSent) {
|
|
16194
16301
|
res.statusCode = 500;
|
|
16195
16302
|
res.setHeader("Content-Type", "text/html; charset=utf-8");
|
|
@@ -16211,7 +16318,7 @@ async function handlePageRequestInternal(options) {
|
|
|
16211
16318
|
abort();
|
|
16212
16319
|
});
|
|
16213
16320
|
}
|
|
16214
|
-
async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env = "dev", config) {
|
|
16321
|
+
async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks, theme, projectRoot, env = "dev", config, notFoundPage) {
|
|
16215
16322
|
try {
|
|
16216
16323
|
const isDataReq = isDataRequest(req);
|
|
16217
16324
|
const skipLayoutHooks = isDataReq && req.headers["x-skip-layout-hooks"] === "true";
|
|
@@ -16220,7 +16327,9 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
|
|
|
16220
16327
|
res,
|
|
16221
16328
|
params: { error: String(error) },
|
|
16222
16329
|
pathname: req.path,
|
|
16223
|
-
locals: { error }
|
|
16330
|
+
locals: { error },
|
|
16331
|
+
Redirect: (destination, permanent = false) => new RedirectResponse(destination, permanent),
|
|
16332
|
+
NotFound: () => new NotFoundResponse()
|
|
16224
16333
|
};
|
|
16225
16334
|
const faviconInfo = projectRoot && config ? getFaviconInfo(projectRoot, config.directories.static, env === "dev") : null;
|
|
16226
16335
|
const layoutProps = {};
|
|
@@ -16253,6 +16362,26 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
|
|
|
16253
16362
|
if (layoutServerHook) {
|
|
16254
16363
|
try {
|
|
16255
16364
|
const layoutResult = await layoutServerHook(ctx);
|
|
16365
|
+
if (layoutResult instanceof RedirectResponse) {
|
|
16366
|
+
if (isDataReq) {
|
|
16367
|
+
res.statusCode = 200;
|
|
16368
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
16369
|
+
res.end(JSON.stringify({ redirect: { destination: layoutResult.destination, permanent: layoutResult.permanent } }));
|
|
16370
|
+
} else {
|
|
16371
|
+
handleRedirect(res, { destination: layoutResult.destination, permanent: layoutResult.permanent });
|
|
16372
|
+
}
|
|
16373
|
+
return;
|
|
16374
|
+
}
|
|
16375
|
+
if (layoutResult instanceof NotFoundResponse) {
|
|
16376
|
+
if (isDataReq) {
|
|
16377
|
+
res.statusCode = 200;
|
|
16378
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
16379
|
+
res.end(JSON.stringify({ notFound: true }));
|
|
16380
|
+
} else {
|
|
16381
|
+
handleNotFound(res, req.path);
|
|
16382
|
+
}
|
|
16383
|
+
return;
|
|
16384
|
+
}
|
|
16256
16385
|
if (layoutResult.props) {
|
|
16257
16386
|
Object.assign(layoutProps, layoutResult.props);
|
|
16258
16387
|
}
|
|
@@ -16270,22 +16399,69 @@ async function renderErrorPageWithStream(errorPage, req, res, error, routeChunks
|
|
|
16270
16399
|
}
|
|
16271
16400
|
}
|
|
16272
16401
|
let loaderResult = await runRouteServerHook(errorPage, ctx);
|
|
16273
|
-
if (
|
|
16274
|
-
|
|
16402
|
+
if (loaderResult instanceof RedirectResponse) {
|
|
16403
|
+
if (isDataReq) {
|
|
16404
|
+
res.statusCode = 200;
|
|
16405
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
16406
|
+
res.end(JSON.stringify({ redirect: { destination: loaderResult.destination, permanent: loaderResult.permanent } }));
|
|
16407
|
+
} else {
|
|
16408
|
+
handleRedirect(res, { destination: loaderResult.destination, permanent: loaderResult.permanent });
|
|
16409
|
+
}
|
|
16410
|
+
return;
|
|
16411
|
+
}
|
|
16412
|
+
if (loaderResult instanceof NotFoundResponse) {
|
|
16413
|
+
if (isDataReq) {
|
|
16414
|
+
res.statusCode = 200;
|
|
16415
|
+
res.setHeader("Content-Type", "application/json; charset=utf-8");
|
|
16416
|
+
res.end(JSON.stringify({ notFound: true }));
|
|
16417
|
+
} else {
|
|
16418
|
+
if (notFoundPage) {
|
|
16419
|
+
const notFoundAssetManifest = env === "prod" && projectRoot ? loadAssetManifest(projectRoot) : null;
|
|
16420
|
+
const notFoundClientJsPath = env === "dev" ? "/static/client.js" : projectRoot ? getClientJsPath(projectRoot) : "/static/client.js";
|
|
16421
|
+
const notFoundClientCssPath = env === "dev" ? "/static/client.css" : projectRoot ? getClientCssPath(projectRoot) : "/static/client.css";
|
|
16422
|
+
const notFoundFaviconInfo = projectRoot && config ? getFaviconInfo(projectRoot, config.directories.static, env === "dev") : null;
|
|
16423
|
+
await renderNotFoundPage(
|
|
16424
|
+
notFoundPage,
|
|
16425
|
+
req,
|
|
16426
|
+
res,
|
|
16427
|
+
req.path,
|
|
16428
|
+
req.path,
|
|
16429
|
+
buildRouterData(req),
|
|
16430
|
+
notFoundAssetManifest,
|
|
16431
|
+
theme,
|
|
16432
|
+
notFoundClientJsPath,
|
|
16433
|
+
notFoundClientCssPath,
|
|
16434
|
+
notFoundFaviconInfo,
|
|
16435
|
+
projectRoot,
|
|
16436
|
+
false,
|
|
16437
|
+
errorPage,
|
|
16438
|
+
routeChunks,
|
|
16439
|
+
env,
|
|
16440
|
+
config
|
|
16441
|
+
);
|
|
16442
|
+
} else {
|
|
16443
|
+
handleNotFound(res, req.path);
|
|
16444
|
+
}
|
|
16445
|
+
}
|
|
16446
|
+
return;
|
|
16447
|
+
}
|
|
16448
|
+
const errorLoaderResult = loaderResult;
|
|
16449
|
+
if (!errorLoaderResult.theme && theme) {
|
|
16450
|
+
errorLoaderResult.theme = theme;
|
|
16275
16451
|
}
|
|
16276
16452
|
const combinedProps = {
|
|
16277
16453
|
...layoutProps,
|
|
16278
|
-
...
|
|
16454
|
+
...errorLoaderResult.props || {}
|
|
16279
16455
|
};
|
|
16280
16456
|
const combinedLoaderResult = {
|
|
16281
|
-
...
|
|
16457
|
+
...errorLoaderResult,
|
|
16282
16458
|
props: combinedProps
|
|
16283
16459
|
};
|
|
16284
16460
|
const initialData = buildInitialData(req.path, { error: String(error) }, combinedLoaderResult);
|
|
16285
16461
|
const routerData = buildRouterData(req);
|
|
16286
16462
|
initialData.error = true;
|
|
16287
16463
|
if (isDataReq) {
|
|
16288
|
-
const pagePropsOnly =
|
|
16464
|
+
const pagePropsOnly = errorLoaderResult.props || {};
|
|
16289
16465
|
handleDataResponse(
|
|
16290
16466
|
res,
|
|
16291
16467
|
combinedLoaderResult,
|
|
@@ -17966,7 +18142,9 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params,
|
|
|
17966
18142
|
res,
|
|
17967
18143
|
params,
|
|
17968
18144
|
pathname: urlPath,
|
|
17969
|
-
locals: {}
|
|
18145
|
+
locals: {},
|
|
18146
|
+
Redirect: (destination, permanent = false) => new RedirectResponse(destination, permanent),
|
|
18147
|
+
NotFound: () => new NotFoundResponse()
|
|
17970
18148
|
};
|
|
17971
18149
|
for (const mw of route.middlewares) {
|
|
17972
18150
|
await Promise.resolve(
|
|
@@ -17982,6 +18160,12 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params,
|
|
|
17982
18160
|
if (layoutServerHook) {
|
|
17983
18161
|
try {
|
|
17984
18162
|
const layoutResult = await layoutServerHook(ctx);
|
|
18163
|
+
if (layoutResult instanceof RedirectResponse || layoutResult instanceof NotFoundResponse) {
|
|
18164
|
+
console.warn(
|
|
18165
|
+
`\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.`
|
|
18166
|
+
);
|
|
18167
|
+
return;
|
|
18168
|
+
}
|
|
17985
18169
|
if (layoutResult.props) {
|
|
17986
18170
|
Object.assign(layoutProps, layoutResult.props);
|
|
17987
18171
|
}
|
|
@@ -18003,10 +18187,17 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params,
|
|
|
18003
18187
|
let loaderResult = { props: {} };
|
|
18004
18188
|
if (route.loader) {
|
|
18005
18189
|
loaderResult = await route.loader(ctx);
|
|
18190
|
+
if (loaderResult instanceof RedirectResponse || loaderResult instanceof NotFoundResponse) {
|
|
18191
|
+
console.warn(
|
|
18192
|
+
`\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.`
|
|
18193
|
+
);
|
|
18194
|
+
return;
|
|
18195
|
+
}
|
|
18006
18196
|
}
|
|
18197
|
+
const pageLoaderResult = loaderResult;
|
|
18007
18198
|
const combinedProps = {
|
|
18008
18199
|
...layoutProps,
|
|
18009
|
-
...
|
|
18200
|
+
...pageLoaderResult.props || {}
|
|
18010
18201
|
};
|
|
18011
18202
|
let combinedMetadata = null;
|
|
18012
18203
|
for (const layoutMeta of layoutMetadata) {
|
|
@@ -18014,17 +18205,14 @@ async function renderStaticRoute(projectRoot, ssgOutDir, route, urlPath, params,
|
|
|
18014
18205
|
combinedMetadata = mergeMetadata(combinedMetadata, layoutMeta);
|
|
18015
18206
|
}
|
|
18016
18207
|
}
|
|
18017
|
-
if (
|
|
18018
|
-
combinedMetadata = mergeMetadata(combinedMetadata,
|
|
18208
|
+
if (pageLoaderResult.metadata) {
|
|
18209
|
+
combinedMetadata = mergeMetadata(combinedMetadata, pageLoaderResult.metadata);
|
|
18019
18210
|
}
|
|
18020
18211
|
const combinedLoaderResult = {
|
|
18021
|
-
...
|
|
18212
|
+
...pageLoaderResult,
|
|
18022
18213
|
props: combinedProps,
|
|
18023
18214
|
metadata: combinedMetadata
|
|
18024
18215
|
};
|
|
18025
|
-
if (combinedLoaderResult.redirect || combinedLoaderResult.notFound) {
|
|
18026
|
-
return;
|
|
18027
|
-
}
|
|
18028
18216
|
const initialData = buildInitialData(urlPath, params, combinedLoaderResult);
|
|
18029
18217
|
const routerData = buildRouterData(req);
|
|
18030
18218
|
const appTree = buildAppTree(route, params, initialData.props);
|
|
@@ -19625,6 +19813,8 @@ var commonSchemas = {
|
|
|
19625
19813
|
export {
|
|
19626
19814
|
DEFAULT_CONFIG,
|
|
19627
19815
|
Logger,
|
|
19816
|
+
NotFoundResponse,
|
|
19817
|
+
RedirectResponse,
|
|
19628
19818
|
ValidationError,
|
|
19629
19819
|
bootstrapClient,
|
|
19630
19820
|
buildApp,
|