@reckona/mreact-router 0.0.138 → 0.0.140
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 +1 -1
- package/dist/actions.d.ts +1 -0
- package/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +32 -12
- package/dist/actions.js.map +1 -1
- package/dist/adapters/static.js +10 -4
- package/dist/adapters/static.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +28 -21
- package/dist/build.js.map +1 -1
- package/dist/cache.d.ts +2 -0
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +23 -1
- package/dist/cache.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +53 -14
- package/dist/client.js.map +1 -1
- package/dist/dev-server.d.ts +1 -0
- package/dist/dev-server.d.ts.map +1 -1
- package/dist/dev-server.js +6 -4
- package/dist/dev-server.js.map +1 -1
- package/dist/link.js +17 -3
- package/dist/link.js.map +1 -1
- package/dist/middleware.d.ts.map +1 -1
- package/dist/middleware.js +10 -7
- package/dist/middleware.js.map +1 -1
- package/dist/multipart.d.ts +5 -0
- package/dist/multipart.d.ts.map +1 -1
- package/dist/multipart.js +24 -1
- package/dist/multipart.js.map +1 -1
- package/dist/native-route-matcher.d.ts +9 -0
- package/dist/native-route-matcher.d.ts.map +1 -1
- package/dist/native-route-matcher.js +23 -11
- package/dist/native-route-matcher.js.map +1 -1
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +8 -1
- package/dist/navigation.js.map +1 -1
- package/dist/render.d.ts +5 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +239 -95
- package/dist/render.js.map +1 -1
- package/dist/route-source.d.ts +3 -1
- package/dist/route-source.d.ts.map +1 -1
- package/dist/route-source.js +13 -11
- package/dist/route-source.js.map +1 -1
- package/dist/route-styles.d.ts +4 -0
- package/dist/route-styles.d.ts.map +1 -1
- package/dist/route-styles.js +15 -3
- package/dist/route-styles.js.map +1 -1
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +39 -9
- package/dist/serve.js.map +1 -1
- package/dist/server-action-inference.d.ts.map +1 -1
- package/dist/server-action-inference.js +57 -0
- package/dist/server-action-inference.js.map +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +27 -11
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/actions.ts +44 -10
- package/src/adapters/static.ts +11 -4
- package/src/build.ts +28 -17
- package/src/cache.ts +33 -1
- package/src/client.ts +53 -14
- package/src/dev-server.ts +11 -4
- package/src/link.ts +24 -3
- package/src/middleware.ts +12 -7
- package/src/multipart.ts +32 -1
- package/src/native-route-matcher.ts +34 -13
- package/src/navigation.ts +11 -2
- package/src/render.ts +321 -113
- package/src/route-source.ts +14 -10
- package/src/route-styles.ts +28 -3
- package/src/serve.ts +68 -15
- package/src/server-action-inference.ts +86 -0
- package/src/vite.ts +39 -12
package/dist/render.js
CHANGED
|
@@ -3,7 +3,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
3
3
|
import { access, readFile, stat } from "node:fs/promises";
|
|
4
4
|
import { dirname, join, relative, resolve, sep } from "node:path";
|
|
5
5
|
import { formatDiagnostic, transform } from "@reckona/mreact-compiler";
|
|
6
|
-
import { createQueryClient, dehydrate, __MREACT_QUERY_STATE_SCRIPT_ID, runWithQueryClient, } from "@reckona/mreact-query";
|
|
6
|
+
import { createQueryClient, dehydrate, __MREACT_QUERY_STATE_SCRIPT_ID, installQueryAsyncStorage, runWithQueryClient, } from "@reckona/mreact-query";
|
|
7
7
|
import { createStringSink, renderAsyncBoundary, renderOutOfOrderReorderScript, renderReactNodeToString, renderToReadableStream, } from "@reckona/mreact-server";
|
|
8
8
|
import { createClientRouteInferenceCache, formatClientRouteInferenceDiagnostic, inferClientRouteModule, } from "./client-route-inference.js";
|
|
9
9
|
import { hydrationMarkerParts, routeIdForPath, withHydrationMarkers, withRouteMarkers, } from "./navigation-runtime.js";
|
|
@@ -36,6 +36,7 @@ const nativeEscapeTransform = {
|
|
|
36
36
|
};
|
|
37
37
|
const authRuntimeStateKey = "__mreactAuthRuntimeState";
|
|
38
38
|
const authSessionScriptId = "__mreact_auth_session";
|
|
39
|
+
installQueryAsyncStorage(new AsyncLocalStorage());
|
|
39
40
|
export async function preloadBuiltRequestModules(options) {
|
|
40
41
|
const clientRouteInferenceCache = createClientRouteInferenceCache();
|
|
41
42
|
const middlewareFiles = [
|
|
@@ -247,7 +248,7 @@ export async function renderAppRequest(options) {
|
|
|
247
248
|
return authStorage.run({}, () => renderAppRequest(options));
|
|
248
249
|
}
|
|
249
250
|
const trace = traceContextFromRequest(options.request);
|
|
250
|
-
const url = new URL(options.request.url);
|
|
251
|
+
const url = options.requestUrl ?? new URL(options.request.url);
|
|
251
252
|
const requestEvent = {
|
|
252
253
|
method: options.request.method,
|
|
253
254
|
path: url.pathname,
|
|
@@ -255,7 +256,7 @@ export async function renderAppRequest(options) {
|
|
|
255
256
|
...(trace === undefined ? {} : { trace }),
|
|
256
257
|
};
|
|
257
258
|
invokeRouterInstrumentation(options.instrumentation?.onRequestStart, requestEvent);
|
|
258
|
-
const response = await renderAppRequestInternal(options);
|
|
259
|
+
const response = await renderAppRequestInternal({ ...options, requestUrl: url });
|
|
259
260
|
invokeRouterInstrumentation(options.instrumentation?.onRequestEnd, {
|
|
260
261
|
...requestEvent,
|
|
261
262
|
status: response.status,
|
|
@@ -333,13 +334,13 @@ export async function resolveAppRouterMiddleware(options) {
|
|
|
333
334
|
}
|
|
334
335
|
async function renderAppRequestInternal(options) {
|
|
335
336
|
const timing = createRenderTiming(options.logger);
|
|
336
|
-
const clientRouteInferenceCache = createClientRouteInferenceCache();
|
|
337
|
+
const clientRouteInferenceCache = options.clientRouteInferenceCache ?? createClientRouteInferenceCache();
|
|
337
338
|
let phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
338
339
|
const routes = options.routes ?? (await scanAppRoutes({ appDir: options.appDir }));
|
|
339
340
|
finishRenderTimingPhase(timing, phaseStartedAt, "routeScanMs");
|
|
340
|
-
const url = new URL(options.request.url);
|
|
341
|
+
const url = options.requestUrl ?? new URL(options.request.url);
|
|
341
342
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
342
|
-
const matched = options.routeMatcher?.match(url.pathname) ?? matchRoute(routes, url.pathname);
|
|
343
|
+
const matched = options.matchedRoute ?? options.routeMatcher?.match(url.pathname) ?? matchRoute(routes, url.pathname);
|
|
343
344
|
finishRenderTimingPhase(timing, phaseStartedAt, "routeMatchMs");
|
|
344
345
|
const hasMiddleware = options.skipMiddleware === true
|
|
345
346
|
? false
|
|
@@ -380,7 +381,9 @@ async function renderAppRequestInternal(options) {
|
|
|
380
381
|
if (middlewareResult.request !== options.request) {
|
|
381
382
|
return renderAppRequestInternal({
|
|
382
383
|
...options,
|
|
384
|
+
matchedRoute: undefined,
|
|
383
385
|
request: middlewareResult.request,
|
|
386
|
+
requestUrl: new URL(middlewareResult.request.url),
|
|
384
387
|
skipMiddleware: true,
|
|
385
388
|
});
|
|
386
389
|
}
|
|
@@ -511,6 +514,7 @@ async function renderAppRequestInternal(options) {
|
|
|
511
514
|
: await cachedRouteResponse({
|
|
512
515
|
cache: options.routeCache,
|
|
513
516
|
key: cacheKey,
|
|
517
|
+
request: options.request,
|
|
514
518
|
});
|
|
515
519
|
finishRenderTimingPhase(timing, phaseStartedAt, "routeCacheMs");
|
|
516
520
|
if (cachedResponse !== undefined) {
|
|
@@ -694,6 +698,7 @@ async function renderAppRequestInternal(options) {
|
|
|
694
698
|
serverModules: options.serverModules,
|
|
695
699
|
serverModuleCacheVersion: options.serverModuleCacheVersion,
|
|
696
700
|
serverSourceFiles: options.serverSourceFiles,
|
|
701
|
+
define: options.define,
|
|
697
702
|
vitePlugins: options.vitePlugins,
|
|
698
703
|
});
|
|
699
704
|
html = injectHeadMetadata(html, metadata);
|
|
@@ -949,6 +954,7 @@ async function renderAppRequestInternal(options) {
|
|
|
949
954
|
cache: options.routeCache,
|
|
950
955
|
path: matched.route.path,
|
|
951
956
|
policy: effectiveCachePolicy,
|
|
957
|
+
request: options.request,
|
|
952
958
|
response,
|
|
953
959
|
});
|
|
954
960
|
finishRenderTimingPhase(timing, phaseStartedAt, "responseBuildMs");
|
|
@@ -1010,7 +1016,7 @@ async function renderAppRequestInternal(options) {
|
|
|
1010
1016
|
vitePlugins: options.vitePlugins,
|
|
1011
1017
|
navigation: recoveryRoute,
|
|
1012
1018
|
status: 500,
|
|
1013
|
-
textFallback:
|
|
1019
|
+
textFallback: "Internal Server Error",
|
|
1014
1020
|
});
|
|
1015
1021
|
emitRenderTiming(options, timing, response.status);
|
|
1016
1022
|
return response;
|
|
@@ -1716,9 +1722,7 @@ async function analyzeRouteSourceUncached(options) {
|
|
|
1716
1722
|
};
|
|
1717
1723
|
}
|
|
1718
1724
|
function routeSourceFilesForAnalysis(options) {
|
|
1719
|
-
return options.
|
|
1720
|
-
? { [options.filename]: options.code }
|
|
1721
|
-
: { ...Object.fromEntries(options.serverSourceFiles), [options.filename]: options.code };
|
|
1725
|
+
return (file) => file === options.filename ? options.code : options.serverSourceFiles?.get(file);
|
|
1722
1726
|
}
|
|
1723
1727
|
async function runServerModule(code, props, sourcefile, serverModules, serverModuleCacheVersion, define, vitePlugins) {
|
|
1724
1728
|
const component = await loadServerComponent(code, sourcefile, serverModules, serverModuleCacheVersion, define, vitePlugins);
|
|
@@ -2126,8 +2130,16 @@ async function applyLayouts(options) {
|
|
|
2126
2130
|
let html = options.html;
|
|
2127
2131
|
let shellHasOutOfOrderBoundary = false;
|
|
2128
2132
|
const slotContext = createSlotRenderContext(options.slots);
|
|
2129
|
-
|
|
2130
|
-
|
|
2133
|
+
const renderShell = (shell) => renderShellPrefixSuffix(options.appDir, shell, options.props, slotContext, options.serverModules, options.serverModuleCacheVersion, options.serverSourceFiles, options.clientRouteInferenceCache, options.timing, options.define, options.vitePlugins, options.importPolicy);
|
|
2134
|
+
const renderedShells = Object.keys(slotContext.namedSlots).length === 0
|
|
2135
|
+
? await Promise.all(layoutFiles.map(renderShell))
|
|
2136
|
+
: undefined;
|
|
2137
|
+
for (let index = layoutFiles.length - 1; index >= 0; index -= 1) {
|
|
2138
|
+
const shell = layoutFiles[index];
|
|
2139
|
+
if (shell === undefined) {
|
|
2140
|
+
continue;
|
|
2141
|
+
}
|
|
2142
|
+
const rendered = renderedShells?.[index] ?? (await renderShell(shell));
|
|
2131
2143
|
shellHasOutOfOrderBoundary ||= rendered.hasOutOfOrderBoundary;
|
|
2132
2144
|
html = `${rendered.prefix}${html}${rendered.suffix}`;
|
|
2133
2145
|
}
|
|
@@ -2148,10 +2160,15 @@ async function applyLayouts(options) {
|
|
|
2148
2160
|
}
|
|
2149
2161
|
async function layoutShellsForPage(appDir, pageFile, props, slots, serverModules, serverModuleCacheVersion, serverSourceFiles, clientRouteInferenceCache, define, vitePlugins, importPolicy) {
|
|
2150
2162
|
const layoutFiles = await shellFilesForPage(appDir, pageFile, serverModuleCacheVersion);
|
|
2151
|
-
const shells = [];
|
|
2152
2163
|
const slotContext = createSlotRenderContext(slots);
|
|
2153
|
-
|
|
2154
|
-
|
|
2164
|
+
const renderShell = (shell) => renderShellPrefixSuffix(appDir, shell, props, slotContext, serverModules, serverModuleCacheVersion, serverSourceFiles, clientRouteInferenceCache, undefined, define, vitePlugins, importPolicy);
|
|
2165
|
+
const shells = Object.keys(slotContext.namedSlots).length === 0
|
|
2166
|
+
? await Promise.all(layoutFiles.map(renderShell))
|
|
2167
|
+
: [];
|
|
2168
|
+
if (shells.length === 0 && layoutFiles.length > 0) {
|
|
2169
|
+
for (const shell of layoutFiles) {
|
|
2170
|
+
shells.push(await renderShell(shell));
|
|
2171
|
+
}
|
|
2155
2172
|
}
|
|
2156
2173
|
warnUnconsumedRouteSlots({
|
|
2157
2174
|
appDir,
|
|
@@ -2172,64 +2189,32 @@ async function renderShellPrefixSuffix(appDir, shell, props, slotContext, server
|
|
|
2172
2189
|
return cached;
|
|
2173
2190
|
}
|
|
2174
2191
|
}
|
|
2175
|
-
|
|
2176
|
-
const
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
const clientInference = artifact !== undefined && artifact.sourceHash === memoizedHashText(code)
|
|
2188
|
-
? {
|
|
2189
|
-
client: false,
|
|
2190
|
-
clientBoundaryImports: [],
|
|
2191
|
-
clientBoundaryFallbackImports: [],
|
|
2192
|
-
diagnostics: [],
|
|
2193
|
-
}
|
|
2194
|
-
: await inferClientRouteModule({
|
|
2195
|
-
cache: clientRouteInferenceCache,
|
|
2196
|
-
code: stripRouteClientOnlyExports(code, shell.file),
|
|
2197
|
-
filename: shell.file,
|
|
2192
|
+
const staticEntry = cacheKey === undefined ? undefined : shellStaticRenderCache.get(cacheKey);
|
|
2193
|
+
const loadedStaticEntry = staticEntry ??
|
|
2194
|
+
(await loadShellStaticRenderEntry({
|
|
2195
|
+
cacheKey,
|
|
2196
|
+
clientRouteInferenceCache,
|
|
2197
|
+
define,
|
|
2198
|
+
importPolicy,
|
|
2199
|
+
serverModuleCacheVersion,
|
|
2200
|
+
serverModules,
|
|
2201
|
+
serverSourceFiles,
|
|
2202
|
+
shell,
|
|
2203
|
+
timing,
|
|
2198
2204
|
vitePlugins,
|
|
2199
|
-
});
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
code,
|
|
2205
|
-
clientBoundaryImports: clientInference.clientBoundaryImports,
|
|
2206
|
-
clientBoundaryFallbackImports: clientInference.clientBoundaryFallbackImports,
|
|
2207
|
-
filename: shell.file,
|
|
2208
|
-
serverModules,
|
|
2209
|
-
serverOutput,
|
|
2210
|
-
});
|
|
2211
|
-
addRenderTimingPhaseDuration(timing, phaseStartedAt, "layoutTransformMs");
|
|
2212
|
-
const fatalDiagnostics = fatalServerDiagnostics(output.diagnostics);
|
|
2213
|
-
if (fatalDiagnostics.length > 0) {
|
|
2214
|
-
throw new Error(formatServerDiagnostics(shell.file, fatalDiagnostics));
|
|
2215
|
-
}
|
|
2216
|
-
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
2217
|
-
const component = shellUsesAwait
|
|
2218
|
-
? selectStreamComponent(await loadServerStreamModule(output.code, shell.file, serverModules, serverModuleCacheVersion, define, vitePlugins, importPolicy))
|
|
2219
|
-
: await loadServerComponent(output.code, shell.file, serverModules, serverModuleCacheVersion, define, vitePlugins, importPolicy);
|
|
2220
|
-
addRenderTimingPhaseDuration(timing, phaseStartedAt, "layoutModuleLoadMs");
|
|
2221
|
-
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
2222
|
-
const layoutHtml = shellUsesAwait
|
|
2223
|
-
? await renderShellStreamComponent(component, props)
|
|
2224
|
-
: await component(props);
|
|
2205
|
+
}));
|
|
2206
|
+
let phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
2207
|
+
const layoutHtml = loadedStaticEntry.shellUsesAwait
|
|
2208
|
+
? await renderShellStreamComponent(loadedStaticEntry.component, props)
|
|
2209
|
+
: await loadedStaticEntry.component(props);
|
|
2225
2210
|
addRenderTimingPhaseDuration(timing, phaseStartedAt, "layoutComponentRenderMs");
|
|
2226
2211
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
2227
2212
|
const rendered = {
|
|
2228
2213
|
...splitLayoutSlot(markShellBoundary(layoutHtml, shell), slotContext),
|
|
2229
|
-
hasOutOfOrderBoundary: hasOutOfOrderBoundary
|
|
2214
|
+
hasOutOfOrderBoundary: loadedStaticEntry.hasOutOfOrderBoundary,
|
|
2230
2215
|
};
|
|
2231
2216
|
addRenderTimingPhaseDuration(timing, phaseStartedAt, "layoutSlotSplitMs");
|
|
2232
|
-
const shellCacheKey = shellUsesAwait ? undefined : cacheKey;
|
|
2217
|
+
const shellCacheKey = loadedStaticEntry.shellUsesAwait ? undefined : cacheKey;
|
|
2233
2218
|
const cached = shellCacheKey !== undefined
|
|
2234
2219
|
? readRouterRuntimeCacheEntry(renderedShellCache, shellCacheKey, renderedShellCacheCounters)
|
|
2235
2220
|
: undefined;
|
|
@@ -2239,7 +2224,7 @@ async function renderShellPrefixSuffix(appDir, shell, props, slotContext, server
|
|
|
2239
2224
|
// entry on the first request that observes the function arity; on
|
|
2240
2225
|
// an "impure" tag we never overwrite it.
|
|
2241
2226
|
if (shellCacheKey !== undefined && cached !== "impure") {
|
|
2242
|
-
if (component.length === 0) {
|
|
2227
|
+
if (loadedStaticEntry.component.length === 0) {
|
|
2243
2228
|
if (renderedShellCache.size >= MAX_RENDERED_SHELL_CACHE_ENTRIES) {
|
|
2244
2229
|
const oldestKey = renderedShellCache.keys().next().value;
|
|
2245
2230
|
if (oldestKey !== undefined) {
|
|
@@ -2258,6 +2243,69 @@ async function renderShellPrefixSuffix(appDir, shell, props, slotContext, server
|
|
|
2258
2243
|
}
|
|
2259
2244
|
return rendered;
|
|
2260
2245
|
}
|
|
2246
|
+
async function loadShellStaticRenderEntry(options) {
|
|
2247
|
+
let phaseStartedAt = renderTimingPhaseStartedAt(options.timing);
|
|
2248
|
+
const code = await readServerSourceFile(options.shell.file, options.serverModuleCacheVersion, options.serverSourceFiles);
|
|
2249
|
+
addRenderTimingPhaseDuration(options.timing, phaseStartedAt, "layoutSourceReadMs");
|
|
2250
|
+
phaseStartedAt = renderTimingPhaseStartedAt(options.timing);
|
|
2251
|
+
const shellUsesAwait = await mayRenderOutOfOrderBoundaryDeep({
|
|
2252
|
+
code,
|
|
2253
|
+
filename: options.shell.file,
|
|
2254
|
+
serverModuleCacheVersion: options.serverModuleCacheVersion,
|
|
2255
|
+
serverSourceFiles: options.serverSourceFiles,
|
|
2256
|
+
});
|
|
2257
|
+
const serverOutput = shellUsesAwait ? "stream" : "string";
|
|
2258
|
+
const artifact = options.serverModules?.get(options.shell.file)?.[serverOutput];
|
|
2259
|
+
const clientInference = artifact !== undefined && artifact.sourceHash === memoizedHashText(code)
|
|
2260
|
+
? {
|
|
2261
|
+
client: false,
|
|
2262
|
+
clientBoundaryImports: [],
|
|
2263
|
+
clientBoundaryFallbackImports: [],
|
|
2264
|
+
diagnostics: [],
|
|
2265
|
+
}
|
|
2266
|
+
: await inferClientRouteModule({
|
|
2267
|
+
cache: options.clientRouteInferenceCache,
|
|
2268
|
+
code: stripRouteClientOnlyExports(code, options.shell.file),
|
|
2269
|
+
filename: options.shell.file,
|
|
2270
|
+
vitePlugins: options.vitePlugins,
|
|
2271
|
+
});
|
|
2272
|
+
for (const diagnostic of clientInference.diagnostics) {
|
|
2273
|
+
console.warn(formatClientRouteInferenceDiagnostic(diagnostic));
|
|
2274
|
+
}
|
|
2275
|
+
const output = transformServerModule({
|
|
2276
|
+
code,
|
|
2277
|
+
clientBoundaryImports: clientInference.clientBoundaryImports,
|
|
2278
|
+
clientBoundaryFallbackImports: clientInference.clientBoundaryFallbackImports,
|
|
2279
|
+
filename: options.shell.file,
|
|
2280
|
+
serverModules: options.serverModules,
|
|
2281
|
+
serverOutput,
|
|
2282
|
+
});
|
|
2283
|
+
addRenderTimingPhaseDuration(options.timing, phaseStartedAt, "layoutTransformMs");
|
|
2284
|
+
const fatalDiagnostics = fatalServerDiagnostics(output.diagnostics);
|
|
2285
|
+
if (fatalDiagnostics.length > 0) {
|
|
2286
|
+
throw new Error(formatServerDiagnostics(options.shell.file, fatalDiagnostics));
|
|
2287
|
+
}
|
|
2288
|
+
phaseStartedAt = renderTimingPhaseStartedAt(options.timing);
|
|
2289
|
+
const component = shellUsesAwait
|
|
2290
|
+
? selectStreamComponent(await loadServerStreamModule(output.code, options.shell.file, options.serverModules, options.serverModuleCacheVersion, options.define, options.vitePlugins, options.importPolicy))
|
|
2291
|
+
: await loadServerComponent(output.code, options.shell.file, options.serverModules, options.serverModuleCacheVersion, options.define, options.vitePlugins, options.importPolicy);
|
|
2292
|
+
addRenderTimingPhaseDuration(options.timing, phaseStartedAt, "layoutModuleLoadMs");
|
|
2293
|
+
const entry = {
|
|
2294
|
+
component,
|
|
2295
|
+
hasOutOfOrderBoundary: hasOutOfOrderBoundary(output.code),
|
|
2296
|
+
shellUsesAwait,
|
|
2297
|
+
};
|
|
2298
|
+
if (options.cacheKey !== undefined) {
|
|
2299
|
+
if (shellStaticRenderCache.size >= MAX_SHELL_STATIC_RENDER_CACHE_ENTRIES) {
|
|
2300
|
+
const oldestKey = shellStaticRenderCache.keys().next().value;
|
|
2301
|
+
if (oldestKey !== undefined) {
|
|
2302
|
+
shellStaticRenderCache.delete(oldestKey);
|
|
2303
|
+
}
|
|
2304
|
+
}
|
|
2305
|
+
shellStaticRenderCache.set(options.cacheKey, entry);
|
|
2306
|
+
}
|
|
2307
|
+
return entry;
|
|
2308
|
+
}
|
|
2261
2309
|
async function renderShellStreamComponent(component, props) {
|
|
2262
2310
|
const sink = createStringSink();
|
|
2263
2311
|
await component(sink, props);
|
|
@@ -2276,11 +2324,17 @@ async function renderShellStreamComponent(component, props) {
|
|
|
2276
2324
|
// next request.
|
|
2277
2325
|
const shellFilesCache = new Map();
|
|
2278
2326
|
const MAX_SHELL_FILES_CACHE_ENTRIES = 1024;
|
|
2327
|
+
const devShellFilesCache = new Map();
|
|
2328
|
+
const shellStaticRenderCache = new Map();
|
|
2329
|
+
const MAX_SHELL_STATIC_RENDER_CACHE_ENTRIES = 1024;
|
|
2279
2330
|
const routeMiddlewareControlCache = new Map();
|
|
2280
2331
|
const MAX_ROUTE_MIDDLEWARE_CONTROL_CACHE_ENTRIES = 1024;
|
|
2281
2332
|
const appMiddlewareFileCache = new Map();
|
|
2282
2333
|
const MAX_APP_MIDDLEWARE_FILE_CACHE_ENTRIES = 1024;
|
|
2334
|
+
const devAppMiddlewareFileCache = new Map();
|
|
2283
2335
|
const routeMiddlewareControlSourceCache = new Map();
|
|
2336
|
+
const devServerSourceFileCache = new Map();
|
|
2337
|
+
const MAX_DEV_SERVER_SOURCE_FILE_CACHE_ENTRIES = 2048;
|
|
2284
2338
|
async function shellFilesForPage(appDir, pageFile, serverModuleCacheVersion) {
|
|
2285
2339
|
const cacheKey = serverModuleCacheVersion === undefined
|
|
2286
2340
|
? undefined
|
|
@@ -2291,6 +2345,31 @@ async function shellFilesForPage(appDir, pageFile, serverModuleCacheVersion) {
|
|
|
2291
2345
|
return cached;
|
|
2292
2346
|
}
|
|
2293
2347
|
}
|
|
2348
|
+
const devCacheKey = cacheKey === undefined ? `${appDir}\0${pageFile}` : undefined;
|
|
2349
|
+
if (devCacheKey !== undefined) {
|
|
2350
|
+
const directoryStats = await routeShellDirectoryStats(appDir, pageFile);
|
|
2351
|
+
const cached = devShellFilesCache.get(devCacheKey);
|
|
2352
|
+
if (cached !== undefined &&
|
|
2353
|
+
sameFileStats(cached.directoryStats, directoryStats)) {
|
|
2354
|
+
return cached.files;
|
|
2355
|
+
}
|
|
2356
|
+
const files = await shellFilesForPageUncached(appDir, pageFile);
|
|
2357
|
+
devShellFilesCache.set(devCacheKey, { directoryStats, files });
|
|
2358
|
+
return files;
|
|
2359
|
+
}
|
|
2360
|
+
const files = await shellFilesForPageUncached(appDir, pageFile);
|
|
2361
|
+
if (cacheKey !== undefined) {
|
|
2362
|
+
if (shellFilesCache.size >= MAX_SHELL_FILES_CACHE_ENTRIES) {
|
|
2363
|
+
const oldestKey = shellFilesCache.keys().next().value;
|
|
2364
|
+
if (oldestKey !== undefined) {
|
|
2365
|
+
shellFilesCache.delete(oldestKey);
|
|
2366
|
+
}
|
|
2367
|
+
}
|
|
2368
|
+
shellFilesCache.set(cacheKey, files);
|
|
2369
|
+
}
|
|
2370
|
+
return files;
|
|
2371
|
+
}
|
|
2372
|
+
async function shellFilesForPageUncached(appDir, pageFile) {
|
|
2294
2373
|
const shells = await existingRouteShellCandidates(appDir, pageFile, async (file) => {
|
|
2295
2374
|
try {
|
|
2296
2375
|
await access(file);
|
|
@@ -2305,15 +2384,6 @@ async function shellFilesForPage(appDir, pageFile, serverModuleCacheVersion) {
|
|
|
2305
2384
|
id: shellBoundaryId(appDir, shell.directory),
|
|
2306
2385
|
kind: shell.kind,
|
|
2307
2386
|
}));
|
|
2308
|
-
if (cacheKey !== undefined) {
|
|
2309
|
-
if (shellFilesCache.size >= MAX_SHELL_FILES_CACHE_ENTRIES) {
|
|
2310
|
-
const oldestKey = shellFilesCache.keys().next().value;
|
|
2311
|
-
if (oldestKey !== undefined) {
|
|
2312
|
-
shellFilesCache.delete(oldestKey);
|
|
2313
|
-
}
|
|
2314
|
-
}
|
|
2315
|
-
shellFilesCache.set(cacheKey, files);
|
|
2316
|
-
}
|
|
2317
2387
|
return files;
|
|
2318
2388
|
}
|
|
2319
2389
|
function withRouteCacheHeader(response, policy) {
|
|
@@ -2339,21 +2409,28 @@ async function hasAppMiddleware(options) {
|
|
|
2339
2409
|
return cached;
|
|
2340
2410
|
}
|
|
2341
2411
|
}
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
|
|
2412
|
+
if (cacheKey === undefined) {
|
|
2413
|
+
const directoryStat = await fileStat(options.appDir);
|
|
2414
|
+
const cached = devAppMiddlewareFileCache.get(options.appDir);
|
|
2415
|
+
if (cached !== undefined &&
|
|
2416
|
+
sameFileStat(cached.directoryStat, directoryStat)) {
|
|
2417
|
+
return cached.hasMiddleware;
|
|
2345
2418
|
}
|
|
2419
|
+
const hasMiddleware = await hasAppMiddlewareUncached(middlewareFiles);
|
|
2420
|
+
devAppMiddlewareFileCache.set(options.appDir, { directoryStat, hasMiddleware });
|
|
2421
|
+
return hasMiddleware;
|
|
2422
|
+
}
|
|
2423
|
+
const loaded = hasAppMiddlewareUncached(middlewareFiles).catch((error) => {
|
|
2424
|
+
appMiddlewareFileCache.delete(cacheKey);
|
|
2346
2425
|
throw error;
|
|
2347
2426
|
});
|
|
2348
|
-
if (
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
appMiddlewareFileCache.delete(oldestKey);
|
|
2353
|
-
}
|
|
2427
|
+
if (appMiddlewareFileCache.size >= MAX_APP_MIDDLEWARE_FILE_CACHE_ENTRIES) {
|
|
2428
|
+
const oldestKey = appMiddlewareFileCache.keys().next().value;
|
|
2429
|
+
if (oldestKey !== undefined) {
|
|
2430
|
+
appMiddlewareFileCache.delete(oldestKey);
|
|
2354
2431
|
}
|
|
2355
|
-
appMiddlewareFileCache.set(cacheKey, loaded);
|
|
2356
2432
|
}
|
|
2433
|
+
appMiddlewareFileCache.set(cacheKey, loaded);
|
|
2357
2434
|
return loaded;
|
|
2358
2435
|
}
|
|
2359
2436
|
async function hasAppMiddlewareUncached(files) {
|
|
@@ -2368,6 +2445,48 @@ async function hasAppMiddlewareUncached(files) {
|
|
|
2368
2445
|
}
|
|
2369
2446
|
return false;
|
|
2370
2447
|
}
|
|
2448
|
+
async function routeShellDirectoryStats(appDir, pageFile) {
|
|
2449
|
+
const directories = [];
|
|
2450
|
+
let current = dirname(pageFile);
|
|
2451
|
+
const resolvedAppDir = resolve(appDir);
|
|
2452
|
+
while (true) {
|
|
2453
|
+
directories.push(current);
|
|
2454
|
+
if (resolve(current) === resolvedAppDir) {
|
|
2455
|
+
break;
|
|
2456
|
+
}
|
|
2457
|
+
const parent = dirname(current);
|
|
2458
|
+
if (parent === current) {
|
|
2459
|
+
break;
|
|
2460
|
+
}
|
|
2461
|
+
current = parent;
|
|
2462
|
+
}
|
|
2463
|
+
return await Promise.all(directories.map((directory) => fileStatOrMissing(directory)));
|
|
2464
|
+
}
|
|
2465
|
+
async function fileStat(file) {
|
|
2466
|
+
const stats = await stat(file);
|
|
2467
|
+
return { mtimeMs: stats.mtimeMs, size: stats.size };
|
|
2468
|
+
}
|
|
2469
|
+
async function fileStatOrMissing(file) {
|
|
2470
|
+
try {
|
|
2471
|
+
return await fileStat(file);
|
|
2472
|
+
}
|
|
2473
|
+
catch (error) {
|
|
2474
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
2475
|
+
return { mtimeMs: -1, size: -1 };
|
|
2476
|
+
}
|
|
2477
|
+
throw error;
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2480
|
+
function sameFileStats(left, right) {
|
|
2481
|
+
return (left.length === right.length &&
|
|
2482
|
+
left.every((value, index) => {
|
|
2483
|
+
const other = right[index];
|
|
2484
|
+
return other !== undefined && sameFileStat(value, other);
|
|
2485
|
+
}));
|
|
2486
|
+
}
|
|
2487
|
+
function sameFileStat(left, right) {
|
|
2488
|
+
return left.mtimeMs === right.mtimeMs && left.size === right.size;
|
|
2489
|
+
}
|
|
2371
2490
|
async function loadRouteMiddlewareControl(options) {
|
|
2372
2491
|
const cacheKey = options.serverModuleCacheVersion === undefined
|
|
2373
2492
|
? undefined
|
|
@@ -2658,12 +2777,11 @@ async function loadComposedRouteMetadataUncached(options) {
|
|
|
2658
2777
|
const layoutFiles = await shellFilesForPage(options.appDir, options.filename, options.serverModuleCacheVersion);
|
|
2659
2778
|
const metadata = [];
|
|
2660
2779
|
let dynamic = hasGenerateMetadataExport(options.code);
|
|
2661
|
-
|
|
2780
|
+
const layoutMetadataTasks = layoutFiles.map(async (shell) => {
|
|
2662
2781
|
if (shell.kind !== "layout") {
|
|
2663
|
-
|
|
2782
|
+
return { code: undefined, metadata: undefined };
|
|
2664
2783
|
}
|
|
2665
2784
|
const code = await readServerSourceFile(shell.file, options.serverModuleCacheVersion, options.serverSourceFiles);
|
|
2666
|
-
dynamic ||= hasGenerateMetadataExport(code);
|
|
2667
2785
|
const shellMetadata = await loadRouteMetadata({
|
|
2668
2786
|
appDir: options.appDir,
|
|
2669
2787
|
code,
|
|
@@ -2675,11 +2793,9 @@ async function loadComposedRouteMetadataUncached(options) {
|
|
|
2675
2793
|
define: options.define,
|
|
2676
2794
|
vitePlugins: options.vitePlugins,
|
|
2677
2795
|
});
|
|
2678
|
-
|
|
2679
|
-
|
|
2680
|
-
|
|
2681
|
-
}
|
|
2682
|
-
const pageMetadata = await loadRouteMetadata({
|
|
2796
|
+
return { code, metadata: shellMetadata };
|
|
2797
|
+
});
|
|
2798
|
+
const pageMetadataTask = loadRouteMetadata({
|
|
2683
2799
|
appDir: options.appDir,
|
|
2684
2800
|
code: options.code,
|
|
2685
2801
|
context: options.context,
|
|
@@ -2690,6 +2806,16 @@ async function loadComposedRouteMetadataUncached(options) {
|
|
|
2690
2806
|
define: options.define,
|
|
2691
2807
|
vitePlugins: options.vitePlugins,
|
|
2692
2808
|
});
|
|
2809
|
+
for (const task of layoutMetadataTasks) {
|
|
2810
|
+
const shell = await task;
|
|
2811
|
+
if (shell.code !== undefined) {
|
|
2812
|
+
dynamic ||= hasGenerateMetadataExport(shell.code);
|
|
2813
|
+
}
|
|
2814
|
+
if (shell.metadata !== undefined) {
|
|
2815
|
+
metadata.push(shell.metadata);
|
|
2816
|
+
}
|
|
2817
|
+
}
|
|
2818
|
+
const pageMetadata = await pageMetadataTask;
|
|
2693
2819
|
if (pageMetadata !== undefined) {
|
|
2694
2820
|
metadata.push(pageMetadata);
|
|
2695
2821
|
}
|
|
@@ -2852,7 +2978,7 @@ function readServerSourceFile(file, serverModuleCacheVersion, serverSourceFiles)
|
|
|
2852
2978
|
return Promise.resolve(manifestSource);
|
|
2853
2979
|
}
|
|
2854
2980
|
if (serverModuleCacheVersion === undefined) {
|
|
2855
|
-
return
|
|
2981
|
+
return readDevServerSourceFile(file);
|
|
2856
2982
|
}
|
|
2857
2983
|
const key = `${serverModuleCacheVersion}:${file}`;
|
|
2858
2984
|
const cached = readRouterRuntimeCacheEntry(serverSourceFileCache, key, serverSourceFileCacheCounters);
|
|
@@ -2866,6 +2992,24 @@ function readServerSourceFile(file, serverModuleCacheVersion, serverSourceFiles)
|
|
|
2866
2992
|
setBoundedCacheEntry(serverSourceFileCache, key, loaded, maxServerSourceFileCacheEntries, serverSourceFileCacheCounters);
|
|
2867
2993
|
return loaded;
|
|
2868
2994
|
}
|
|
2995
|
+
async function readDevServerSourceFile(file) {
|
|
2996
|
+
const stats = await fileStat(file);
|
|
2997
|
+
const cached = devServerSourceFileCache.get(file);
|
|
2998
|
+
if (cached !== undefined &&
|
|
2999
|
+
cached.mtimeMs === stats.mtimeMs &&
|
|
3000
|
+
cached.size === stats.size) {
|
|
3001
|
+
return cached.source;
|
|
3002
|
+
}
|
|
3003
|
+
const source = await readFile(file, "utf8");
|
|
3004
|
+
if (devServerSourceFileCache.size >= MAX_DEV_SERVER_SOURCE_FILE_CACHE_ENTRIES) {
|
|
3005
|
+
const oldestKey = devServerSourceFileCache.keys().next().value;
|
|
3006
|
+
if (oldestKey !== undefined) {
|
|
3007
|
+
devServerSourceFileCache.delete(oldestKey);
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
devServerSourceFileCache.set(file, { ...stats, source });
|
|
3011
|
+
return source;
|
|
3012
|
+
}
|
|
2869
3013
|
function setBoundedCacheEntry(cache, key, value, maxEntries, counters) {
|
|
2870
3014
|
if (cache.size >= maxEntries) {
|
|
2871
3015
|
const oldestKey = cache.keys().next().value;
|