@mandujs/core 0.39.0 → 0.39.2
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/package.json +1 -1
- package/src/runtime/server.ts +81 -6
package/package.json
CHANGED
package/src/runtime/server.ts
CHANGED
|
@@ -1173,6 +1173,72 @@ export function setCreateApp(fn: CreateAppFn): void {
|
|
|
1173
1173
|
defaultRegistry.setCreateApp(fn);
|
|
1174
1174
|
}
|
|
1175
1175
|
|
|
1176
|
+
/**
|
|
1177
|
+
* Issue #232 follow-up — eager page-component registration.
|
|
1178
|
+
*
|
|
1179
|
+
* After a dev-mode HMR reload, `registerPageHandler` / `registerPageLoader`
|
|
1180
|
+
* install lazy thunks. The actual component import and
|
|
1181
|
+
* `registerRouteComponent` call is deferred until the first request for
|
|
1182
|
+
* that route. On a brand-new browser reload triggered by HMR the request
|
|
1183
|
+
* normally populates the registry in time, but any code path that renders
|
|
1184
|
+
* without going through `loadPageData` first (or a thrown error that
|
|
1185
|
+
* short-circuits it) leaves `routeComponents` empty, and the default
|
|
1186
|
+
* appCreator renders the "404 - Route Not Found" fallback even though the
|
|
1187
|
+
* route is valid.
|
|
1188
|
+
*
|
|
1189
|
+
* `prewarmPageRoutes()` walks every registered `pageHandler` / `pageLoader`
|
|
1190
|
+
* once and drives it through the same code path the first request would
|
|
1191
|
+
* take, so `routeComponents` is fully populated before the server starts
|
|
1192
|
+
* serving. The cost is a one-time module import per page route — trivial
|
|
1193
|
+
* in dev. Production `mandu start` is unaffected because it already runs
|
|
1194
|
+
* requests through the same lazy path without HMR races.
|
|
1195
|
+
*/
|
|
1196
|
+
export async function prewarmPageRoutes(
|
|
1197
|
+
registry: ServerRegistry = defaultRegistry,
|
|
1198
|
+
): Promise<{ prewarmed: string[]; failed: Array<{ routeId: string; error: string }> }> {
|
|
1199
|
+
const prewarmed: string[] = [];
|
|
1200
|
+
const failed: Array<{ routeId: string; error: string }> = [];
|
|
1201
|
+
|
|
1202
|
+
// pageHandlers path (routes with .slot.ts / filling)
|
|
1203
|
+
for (const routeId of registry.pageHandlers.keys()) {
|
|
1204
|
+
try {
|
|
1205
|
+
await ensurePageRouteMetadata(routeId, registry);
|
|
1206
|
+
prewarmed.push(routeId);
|
|
1207
|
+
} catch (err) {
|
|
1208
|
+
failed.push({
|
|
1209
|
+
routeId,
|
|
1210
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1211
|
+
});
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// pageLoaders path (plain page components, no slot)
|
|
1216
|
+
for (const routeId of registry.pageLoaders.keys()) {
|
|
1217
|
+
if (registry.routeComponents.has(routeId)) continue; // already warm
|
|
1218
|
+
try {
|
|
1219
|
+
const loader = registry.pageLoaders.get(routeId);
|
|
1220
|
+
if (!loader) continue;
|
|
1221
|
+
const module = await loader();
|
|
1222
|
+
const exported: unknown = module.default;
|
|
1223
|
+
const exportedObj = exported as Record<string, unknown> | null;
|
|
1224
|
+
const component = typeof exported === "function"
|
|
1225
|
+
? (exported as RouteComponent)
|
|
1226
|
+
: (exportedObj?.component as RouteComponent | undefined);
|
|
1227
|
+
if (typeof component === "function") {
|
|
1228
|
+
registry.registerRouteComponent(routeId, component);
|
|
1229
|
+
prewarmed.push(routeId);
|
|
1230
|
+
}
|
|
1231
|
+
} catch (err) {
|
|
1232
|
+
failed.push({
|
|
1233
|
+
routeId,
|
|
1234
|
+
error: err instanceof Error ? err.message : String(err),
|
|
1235
|
+
});
|
|
1236
|
+
}
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
return { prewarmed, failed };
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1176
1242
|
/**
|
|
1177
1243
|
* Layout 로더 등록 (전역)
|
|
1178
1244
|
*/
|
|
@@ -3785,13 +3851,22 @@ async function handleRequestInternal(
|
|
|
3785
3851
|
// Must run BEFORE static-file serving and route dispatch so that
|
|
3786
3852
|
// `mandu build`-emitted HTML short-circuits SSR. No-op if the
|
|
3787
3853
|
// feature is disabled or the path wasn't prerendered.
|
|
3788
|
-
|
|
3789
|
-
|
|
3790
|
-
|
|
3791
|
-
|
|
3792
|
-
|
|
3854
|
+
//
|
|
3855
|
+
// #232 — In dev mode we SKIP the prerender cache entirely. The cache is
|
|
3856
|
+
// populated by `mandu build` and never invalidated during `mandu dev`, so
|
|
3857
|
+
// keeping it in the dev path causes the browser to see stale HTML after
|
|
3858
|
+
// HMR signals "full reload" — user saves a file, reloads, and the old
|
|
3859
|
+
// prerendered output wins. Production `mandu start` always runs with
|
|
3860
|
+
// `isDev: false`, so prod behavior is unchanged.
|
|
3861
|
+
if (!settings.isDev) {
|
|
3862
|
+
const prerendered = await tryServePrerendered(pathname, settings, req.method, req);
|
|
3863
|
+
if (prerendered) {
|
|
3864
|
+
if (settings.cors && isCorsRequest(req)) {
|
|
3865
|
+
const corsOptions: CorsOptions = typeof settings.cors === 'object' ? settings.cors : {};
|
|
3866
|
+
return ok(applyCorsToResponse(prerendered, req, corsOptions));
|
|
3867
|
+
}
|
|
3868
|
+
return ok(prerendered);
|
|
3793
3869
|
}
|
|
3794
|
-
return ok(prerendered);
|
|
3795
3870
|
}
|
|
3796
3871
|
|
|
3797
3872
|
// ─── Phase 18.μ — i18n dispatch ─────────────────────────────────────────
|