@reckona/mreact-router 0.0.180 → 0.0.182
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/dist/actions.d.ts.map +1 -1
- package/dist/actions.js +20 -8
- package/dist/actions.js.map +1 -1
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +53 -18
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +65 -12
- package/dist/build.js.map +1 -1
- package/dist/built-assets.d.ts.map +1 -1
- package/dist/built-assets.js +2 -2
- package/dist/built-assets.js.map +1 -1
- package/dist/cache-config.d.ts +1 -1
- package/dist/cache-config.d.ts.map +1 -1
- package/dist/cache-config.js.map +1 -1
- package/dist/cache.d.ts.map +1 -1
- package/dist/cache.js +18 -1
- package/dist/cache.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +16 -6
- package/dist/client.js.map +1 -1
- package/dist/csrf.js +10 -6
- package/dist/csrf.js.map +1 -1
- package/dist/navigation.d.ts.map +1 -1
- package/dist/navigation.js +14 -1
- package/dist/navigation.js.map +1 -1
- package/dist/render.d.ts +2 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +81 -38
- package/dist/render.js.map +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +5 -3
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/actions.ts +26 -9
- package/src/adapters/cloudflare.ts +98 -41
- package/src/build.ts +93 -30
- package/src/built-assets.ts +2 -3
- package/src/cache-config.ts +1 -0
- package/src/cache.ts +20 -1
- package/src/client.ts +16 -6
- package/src/csrf.ts +13 -6
- package/src/navigation.ts +16 -1
- package/src/render.ts +168 -70
- package/src/vite.ts +6 -3
package/src/csrf.ts
CHANGED
|
@@ -8,7 +8,7 @@ const formFieldCsrf = "__mreact_csrf";
|
|
|
8
8
|
export const formCsrfFieldName = formFieldCsrf;
|
|
9
9
|
|
|
10
10
|
export function serverActionCookie(csrfToken: string): string {
|
|
11
|
-
const
|
|
11
|
+
const secure = shouldUseSecureCsrfCookie();
|
|
12
12
|
const parts = [
|
|
13
13
|
`${currentCsrfCookieName()}=${encodeURIComponent(csrfToken)}`,
|
|
14
14
|
"Path=/",
|
|
@@ -16,7 +16,7 @@ export function serverActionCookie(csrfToken: string): string {
|
|
|
16
16
|
"HttpOnly",
|
|
17
17
|
];
|
|
18
18
|
|
|
19
|
-
if (
|
|
19
|
+
if (secure) {
|
|
20
20
|
parts.push("Secure");
|
|
21
21
|
}
|
|
22
22
|
|
|
@@ -97,16 +97,23 @@ function randomToken(): string {
|
|
|
97
97
|
return `${hex.slice(0, 4).join("")}-${hex.slice(4, 6).join("")}-${hex.slice(6, 8).join("")}-${hex.slice(8, 10).join("")}-${hex.slice(10).join("")}`;
|
|
98
98
|
}
|
|
99
99
|
|
|
100
|
-
function
|
|
101
|
-
return
|
|
100
|
+
function isLocalCsrfEnvironment(): boolean {
|
|
101
|
+
return (
|
|
102
|
+
typeof process !== "undefined" &&
|
|
103
|
+
(process.env?.NODE_ENV === "development" || process.env?.NODE_ENV === "test")
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function shouldUseSecureCsrfCookie(): boolean {
|
|
108
|
+
return !isLocalCsrfEnvironment();
|
|
102
109
|
}
|
|
103
110
|
|
|
104
111
|
function currentCsrfCookieName(): string {
|
|
105
|
-
return
|
|
112
|
+
return shouldUseSecureCsrfCookie() ? csrfCookieNameProduction : csrfCookieNameDevelopment;
|
|
106
113
|
}
|
|
107
114
|
|
|
108
115
|
function csrfCookieNamesRead(): readonly string[] {
|
|
109
|
-
return
|
|
116
|
+
return shouldUseSecureCsrfCookie()
|
|
110
117
|
? [csrfCookieNameProduction]
|
|
111
118
|
: [csrfCookieNameProduction, csrfCookieNameDevelopment];
|
|
112
119
|
}
|
package/src/navigation.ts
CHANGED
|
@@ -31,11 +31,23 @@ function stripLeadingControlOrWhitespace(value: string): string {
|
|
|
31
31
|
return value.slice(start);
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
+
function containsControlCharacter(value: string): boolean {
|
|
35
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
36
|
+
const code = value.charCodeAt(index);
|
|
37
|
+
if (code <= 0x1f || code === 0x7f) {
|
|
38
|
+
return true;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
|
|
34
45
|
// Returns true if `location` is safe to use as a same-origin Location header.
|
|
35
46
|
// Allowed: path-absolute (`/foo`), query-only (`?x=1`), hash-only (`#x`),
|
|
36
47
|
// relative (`foo`). Rejected: protocol-relative (`//evil`), backslash variants
|
|
37
48
|
// (`/\evil`, `\\evil`), and anything with a scheme like `javascript:`.
|
|
38
49
|
function isSafeInternalRedirect(location: string): boolean {
|
|
50
|
+
if (containsControlCharacter(location)) return false;
|
|
39
51
|
const trimmed = stripLeadingControlOrWhitespace(location);
|
|
40
52
|
if (trimmed === "") return false;
|
|
41
53
|
if (trimmed.startsWith("//")) return false;
|
|
@@ -47,6 +59,7 @@ function isSafeInternalRedirect(location: string): boolean {
|
|
|
47
59
|
}
|
|
48
60
|
|
|
49
61
|
function isSafeExternalRedirect(location: string): boolean {
|
|
62
|
+
if (containsControlCharacter(location)) return false;
|
|
50
63
|
const trimmed = stripLeadingControlOrWhitespace(location);
|
|
51
64
|
const match = /^([a-zA-Z][a-zA-Z0-9+.-]*):/.exec(trimmed);
|
|
52
65
|
if (match === null || match[1] === undefined) return false;
|
|
@@ -61,7 +74,9 @@ function throwUnsafeRedirect(location: string): never {
|
|
|
61
74
|
}
|
|
62
75
|
|
|
63
76
|
function throwUnsafeRewrite(location: string): never {
|
|
64
|
-
throw new TypeError(
|
|
77
|
+
throw new TypeError(
|
|
78
|
+
`unsafe rewrite target: ${JSON.stringify(location)} - rewrite() only accepts same-origin paths, query strings, or hashes; external, protocol-relative, backslash, and control-character targets are rejected`,
|
|
79
|
+
);
|
|
65
80
|
}
|
|
66
81
|
|
|
67
82
|
/**
|
package/src/render.ts
CHANGED
|
@@ -205,6 +205,7 @@ export interface RenderAppRequestOptions {
|
|
|
205
205
|
routes?: readonly AppRoute[] | undefined;
|
|
206
206
|
serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
|
|
207
207
|
serverModuleCacheVersion?: string | undefined;
|
|
208
|
+
devServerModuleCacheVersion?: string | undefined;
|
|
208
209
|
// True when serving through a dev server: request-time server transforms
|
|
209
210
|
// are expected there and must not trigger the production prebuild warning.
|
|
210
211
|
dev?: boolean | undefined;
|
|
@@ -251,10 +252,9 @@ export async function preloadBuiltRequestModules(options: {
|
|
|
251
252
|
serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
|
|
252
253
|
serverModuleCacheVersion: string;
|
|
253
254
|
serverSourceFiles: ReadonlyMap<string, string>;
|
|
254
|
-
serverActionReferencesByFile?:
|
|
255
|
-
string,
|
|
256
|
-
|
|
257
|
-
> | undefined;
|
|
255
|
+
serverActionReferencesByFile?:
|
|
256
|
+
| ReadonlyMap<string, readonly PreparedFormActionReference[]>
|
|
257
|
+
| undefined;
|
|
258
258
|
vitePlugins?: readonly PluginOption[] | undefined;
|
|
259
259
|
}): Promise<void> {
|
|
260
260
|
const clientRouteInferenceCache = createClientRouteInferenceCache();
|
|
@@ -535,6 +535,8 @@ const routeOutOfOrderBoundaryAnalysisCache = new Map<string, Promise<boolean>>()
|
|
|
535
535
|
const routeOutOfOrderBoundaryAnalysisCacheCounters = createRouterRuntimeCacheCounters();
|
|
536
536
|
const routeLoaderModuleCache = new Map<string, Promise<RouteLoaderModule>>();
|
|
537
537
|
const routeLoaderModuleCacheCounters = createRouterRuntimeCacheCounters();
|
|
538
|
+
const routeMetadataModuleCache = new Map<string, Promise<RouteMetadataModule>>();
|
|
539
|
+
const routeMetadataModuleCacheCounters = createRouterRuntimeCacheCounters();
|
|
538
540
|
const middlewareModuleCache = new Map<string, Promise<MiddlewareModule>>();
|
|
539
541
|
const middlewareModuleCacheCounters = createRouterRuntimeCacheCounters();
|
|
540
542
|
const serverRouteModuleCache = new Map<string, Promise<Record<string, unknown>>>();
|
|
@@ -549,6 +551,7 @@ const maxRouteOutOfOrderBoundaryAnalysisCacheEntries = resolveRouterCacheLimit(
|
|
|
549
551
|
512,
|
|
550
552
|
);
|
|
551
553
|
const maxRouteLoaderModuleCacheEntries = resolveRouterCacheLimit("ROUTE_LOADER_MODULE", 512);
|
|
554
|
+
const maxRouteMetadataModuleCacheEntries = resolveRouterCacheLimit("ROUTE_METADATA_MODULE", 512);
|
|
552
555
|
const maxMiddlewareModuleCacheEntries = resolveRouterCacheLimit("MIDDLEWARE_MODULE", 64);
|
|
553
556
|
const maxServerRouteModuleCacheEntries = resolveRouterCacheLimit("SERVER_ROUTE_MODULE", 512);
|
|
554
557
|
const maxComposedRouteMetadataCacheEntries = resolveRouterCacheLimit(
|
|
@@ -588,6 +591,12 @@ export function routerRenderRuntimeCacheStats(): RouterRuntimeCacheStat[] {
|
|
|
588
591
|
maxRouteLoaderModuleCacheEntries,
|
|
589
592
|
routeLoaderModuleCacheCounters,
|
|
590
593
|
),
|
|
594
|
+
routerRuntimeCacheStat(
|
|
595
|
+
"route-metadata-module",
|
|
596
|
+
routeMetadataModuleCache,
|
|
597
|
+
maxRouteMetadataModuleCacheEntries,
|
|
598
|
+
routeMetadataModuleCacheCounters,
|
|
599
|
+
),
|
|
591
600
|
routerRuntimeCacheStat(
|
|
592
601
|
"middleware-module",
|
|
593
602
|
middlewareModuleCache,
|
|
@@ -718,9 +727,11 @@ function addRenderTimingPhaseDuration(
|
|
|
718
727
|
}
|
|
719
728
|
|
|
720
729
|
function routeLoaderMayReturnControlResponse(code: string): boolean {
|
|
721
|
-
return
|
|
730
|
+
return (
|
|
731
|
+
/\b(?:redirect|redirectExternal|rewrite|notFound|throwNotFound)\s*\(/.test(code) ||
|
|
722
732
|
/\bnew\s+Response\s*\(/.test(code) ||
|
|
723
|
-
/\bResponse\.(?:redirect|json)\s*\(/.test(code)
|
|
733
|
+
/\bResponse\.(?:redirect|json)\s*\(/.test(code)
|
|
734
|
+
);
|
|
724
735
|
}
|
|
725
736
|
|
|
726
737
|
async function waitForRenderPreload(
|
|
@@ -811,7 +822,9 @@ export async function resolveAppRouterMiddleware(options: {
|
|
|
811
822
|
return { response: middlewareResponse, type: "response" };
|
|
812
823
|
}
|
|
813
824
|
|
|
814
|
-
async function renderAppRequestInternal(
|
|
825
|
+
async function renderAppRequestInternal(
|
|
826
|
+
options: RenderAppRequestRuntimeOptions,
|
|
827
|
+
): Promise<Response> {
|
|
815
828
|
warnProductionRenderWithoutPrebuiltModules(options);
|
|
816
829
|
const timing = createRenderTiming(options.logger);
|
|
817
830
|
const clientRouteInferenceCache =
|
|
@@ -822,7 +835,9 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
|
|
|
822
835
|
const url = options.requestUrl ?? new URL(options.request.url);
|
|
823
836
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
824
837
|
const matched =
|
|
825
|
-
options.matchedRoute ??
|
|
838
|
+
options.matchedRoute ??
|
|
839
|
+
options.routeMatcher?.match(url.pathname) ??
|
|
840
|
+
matchRoute(routes, url.pathname);
|
|
826
841
|
finishRenderTimingPhase(timing, phaseStartedAt, "routeMatchMs");
|
|
827
842
|
const hasMiddleware =
|
|
828
843
|
options.skipMiddleware === true
|
|
@@ -1174,6 +1189,7 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
|
|
|
1174
1189
|
options.vitePlugins,
|
|
1175
1190
|
undefined,
|
|
1176
1191
|
options.importPolicy,
|
|
1192
|
+
options.devServerModuleCacheVersion,
|
|
1177
1193
|
),
|
|
1178
1194
|
);
|
|
1179
1195
|
const pageHtml = renderedPage.html;
|
|
@@ -1230,6 +1246,7 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
|
|
|
1230
1246
|
routes,
|
|
1231
1247
|
serverModules: options.serverModules,
|
|
1232
1248
|
serverModuleCacheVersion: options.serverModuleCacheVersion,
|
|
1249
|
+
devServerModuleCacheVersion: options.devServerModuleCacheVersion,
|
|
1233
1250
|
serverSourceFiles: options.serverSourceFiles,
|
|
1234
1251
|
define: options.define,
|
|
1235
1252
|
vitePlugins: options.vitePlugins,
|
|
@@ -1448,6 +1465,7 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
|
|
|
1448
1465
|
options.vitePlugins,
|
|
1449
1466
|
timing,
|
|
1450
1467
|
options.importPolicy,
|
|
1468
|
+
options.devServerModuleCacheVersion,
|
|
1451
1469
|
),
|
|
1452
1470
|
);
|
|
1453
1471
|
finishRenderTimingPhase(timing, phaseStartedAt, "pageRenderMs");
|
|
@@ -1510,12 +1528,13 @@ async function renderAppRequestInternal(options: RenderAppRequestRuntimeOptions)
|
|
|
1510
1528
|
filename: matched.route.file,
|
|
1511
1529
|
importPolicy: options.importPolicy,
|
|
1512
1530
|
routes,
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1531
|
+
serverModules: options.serverModules,
|
|
1532
|
+
serverModuleCacheVersion: options.serverModuleCacheVersion,
|
|
1533
|
+
devServerModuleCacheVersion: options.devServerModuleCacheVersion,
|
|
1534
|
+
serverSourceFiles: options.serverSourceFiles,
|
|
1535
|
+
define: options.define,
|
|
1536
|
+
vitePlugins: options.vitePlugins,
|
|
1537
|
+
});
|
|
1519
1538
|
finishRenderTimingPhase(timing, phaseStartedAt, "metadataMs");
|
|
1520
1539
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
1521
1540
|
html = injectHeadMetadata(html, metadata);
|
|
@@ -2969,6 +2988,7 @@ async function runServerModuleWithSlots(
|
|
|
2969
2988
|
vitePlugins?: readonly PluginOption[] | undefined,
|
|
2970
2989
|
timing?: RenderTiming | undefined,
|
|
2971
2990
|
importPolicy?: AppRouterImportPolicy | undefined,
|
|
2991
|
+
devServerModuleCacheVersion?: string | undefined,
|
|
2972
2992
|
): Promise<{ html: string; slots: Record<string, string> }> {
|
|
2973
2993
|
const moduleLoadStartedAt = renderTimingPhaseStartedAt(timing);
|
|
2974
2994
|
const module = await loadServerModule(
|
|
@@ -2979,6 +2999,7 @@ async function runServerModuleWithSlots(
|
|
|
2979
2999
|
define,
|
|
2980
3000
|
vitePlugins,
|
|
2981
3001
|
importPolicy,
|
|
3002
|
+
devServerModuleCacheVersion,
|
|
2982
3003
|
);
|
|
2983
3004
|
finishRenderTimingPhase(timing, moduleLoadStartedAt, "pageModuleLoadMs");
|
|
2984
3005
|
const component = selectServerComponent(module);
|
|
@@ -3003,6 +3024,7 @@ async function loadServerModule(
|
|
|
3003
3024
|
define?: UserConfig["define"] | undefined,
|
|
3004
3025
|
vitePlugins?: readonly PluginOption[] | undefined,
|
|
3005
3026
|
importPolicy?: AppRouterImportPolicy | undefined,
|
|
3027
|
+
devServerModuleCacheVersion?: string | undefined,
|
|
3006
3028
|
): Promise<ServerModuleExports> {
|
|
3007
3029
|
const artifact = serverModules?.get(sourcefile)?.string;
|
|
3008
3030
|
const codeHash = memoizedHashText(code);
|
|
@@ -3020,11 +3042,14 @@ async function loadServerModule(
|
|
|
3020
3042
|
});
|
|
3021
3043
|
}
|
|
3022
3044
|
const moduleCode =
|
|
3023
|
-
prebuiltCode === undefined
|
|
3045
|
+
prebuiltCode === undefined
|
|
3046
|
+
? code
|
|
3047
|
+
: rewriteCompatVendorPlaceholderImportsForRunner(prebuiltCode);
|
|
3048
|
+
const sourceModuleCacheVersion = serverModuleCacheVersion ?? devServerModuleCacheVersion;
|
|
3024
3049
|
const cacheKey =
|
|
3025
|
-
|
|
3050
|
+
sourceModuleCacheVersion === undefined
|
|
3026
3051
|
? undefined
|
|
3027
|
-
: `server-component:${serverModuleCacheVersion}:${sourcefile}:${
|
|
3052
|
+
: `server-component:${serverModuleCacheVersion === undefined ? "dev" : "build"}:${sourceModuleCacheVersion}:${sourcefile}:${
|
|
3028
3053
|
moduleCode === code ? codeHash : memoizedHashText(moduleCode)
|
|
3029
3054
|
}:${importPolicyCacheKey(importPolicy)}:${viteDefineCacheKey(define)}:${vitePluginsCacheKey(vitePlugins)}`;
|
|
3030
3055
|
return await importAppRouterSourceModule<ServerModuleExports>({
|
|
@@ -3663,7 +3688,12 @@ async function loadServerStreamModule(
|
|
|
3663
3688
|
): Promise<StreamModuleExports> {
|
|
3664
3689
|
const artifactCode = serverModules?.get(sourcefile)?.stream;
|
|
3665
3690
|
const codeHash = memoizedHashText(code);
|
|
3666
|
-
const prebuiltCode = prebuiltServerComponentModuleCode(
|
|
3691
|
+
const prebuiltCode = prebuiltServerComponentModuleCode(
|
|
3692
|
+
artifactCode,
|
|
3693
|
+
code,
|
|
3694
|
+
codeHash,
|
|
3695
|
+
outputOptions,
|
|
3696
|
+
);
|
|
3667
3697
|
const usesPrebuiltCompiledCode = artifactCode?.code === code;
|
|
3668
3698
|
if (
|
|
3669
3699
|
artifactCode !== undefined &&
|
|
@@ -3678,7 +3708,9 @@ async function loadServerStreamModule(
|
|
|
3678
3708
|
});
|
|
3679
3709
|
}
|
|
3680
3710
|
const moduleCode =
|
|
3681
|
-
prebuiltCode === undefined
|
|
3711
|
+
prebuiltCode === undefined
|
|
3712
|
+
? code
|
|
3713
|
+
: rewriteCompatVendorPlaceholderImportsForRunner(prebuiltCode);
|
|
3682
3714
|
const cacheKey =
|
|
3683
3715
|
serverModuleCacheVersion === undefined
|
|
3684
3716
|
? undefined
|
|
@@ -3817,19 +3849,19 @@ async function layoutShellsForPage(
|
|
|
3817
3849
|
const slotContext = createSlotRenderContext(slots);
|
|
3818
3850
|
const renderShell = (shell: ShellFile) =>
|
|
3819
3851
|
renderShellPrefixSuffix(
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3852
|
+
appDir,
|
|
3853
|
+
shell,
|
|
3854
|
+
props,
|
|
3855
|
+
slotContext,
|
|
3856
|
+
serverModules,
|
|
3857
|
+
serverModuleCacheVersion,
|
|
3858
|
+
serverSourceFiles,
|
|
3859
|
+
clientRouteInferenceCache,
|
|
3860
|
+
undefined,
|
|
3861
|
+
define,
|
|
3862
|
+
vitePlugins,
|
|
3863
|
+
importPolicy,
|
|
3864
|
+
);
|
|
3833
3865
|
const shells =
|
|
3834
3866
|
Object.keys(slotContext.namedSlots).length === 0
|
|
3835
3867
|
? await Promise.all(layoutFiles.map(renderShell))
|
|
@@ -3881,8 +3913,7 @@ async function renderShellPrefixSuffix(
|
|
|
3881
3913
|
}
|
|
3882
3914
|
}
|
|
3883
3915
|
|
|
3884
|
-
const staticEntry =
|
|
3885
|
-
cacheKey === undefined ? undefined : shellStaticRenderCache.get(cacheKey);
|
|
3916
|
+
const staticEntry = cacheKey === undefined ? undefined : shellStaticRenderCache.get(cacheKey);
|
|
3886
3917
|
const loadedStaticEntry =
|
|
3887
3918
|
staticEntry ??
|
|
3888
3919
|
(await loadShellStaticRenderEntry({
|
|
@@ -4065,19 +4096,28 @@ async function renderShellStreamComponent(
|
|
|
4065
4096
|
// next request.
|
|
4066
4097
|
const shellFilesCache = new Map<string, ShellFile[]>();
|
|
4067
4098
|
const MAX_SHELL_FILES_CACHE_ENTRIES = 1024;
|
|
4068
|
-
const devShellFilesCache = new Map<
|
|
4099
|
+
const devShellFilesCache = new Map<
|
|
4100
|
+
string,
|
|
4101
|
+
{ directoryStats: readonly FileStat[]; files: ShellFile[] }
|
|
4102
|
+
>();
|
|
4069
4103
|
const shellStaticRenderCache = new Map<string, ShellStaticRenderEntry>();
|
|
4070
4104
|
const MAX_SHELL_STATIC_RENDER_CACHE_ENTRIES = 1024;
|
|
4071
4105
|
const routeMiddlewareControlCache = new Map<string, Promise<RouteMiddlewareControl | undefined>>();
|
|
4072
4106
|
const MAX_ROUTE_MIDDLEWARE_CONTROL_CACHE_ENTRIES = 1024;
|
|
4073
4107
|
const appMiddlewareFileCache = new Map<string, Promise<boolean>>();
|
|
4074
4108
|
const MAX_APP_MIDDLEWARE_FILE_CACHE_ENTRIES = 1024;
|
|
4075
|
-
const devAppMiddlewareFileCache = new Map<
|
|
4109
|
+
const devAppMiddlewareFileCache = new Map<
|
|
4110
|
+
string,
|
|
4111
|
+
{ directoryStat: FileStat; hasMiddleware: boolean }
|
|
4112
|
+
>();
|
|
4076
4113
|
const routeMiddlewareControlSourceCache = new Map<
|
|
4077
4114
|
string,
|
|
4078
4115
|
{ control: RouteMiddlewareControl | undefined; mtimeMs: number }
|
|
4079
4116
|
>();
|
|
4080
|
-
const devServerSourceFileCache = new Map<
|
|
4117
|
+
const devServerSourceFileCache = new Map<
|
|
4118
|
+
string,
|
|
4119
|
+
{ mtimeMs: number; size: number; source: string }
|
|
4120
|
+
>();
|
|
4081
4121
|
const MAX_DEV_SERVER_SOURCE_FILE_CACHE_ENTRIES = 2048;
|
|
4082
4122
|
|
|
4083
4123
|
interface FileStat {
|
|
@@ -4111,10 +4151,7 @@ async function shellFilesForPage(
|
|
|
4111
4151
|
if (devCacheKey !== undefined) {
|
|
4112
4152
|
const directoryStats = await routeShellDirectoryStats(appDir, pageFile);
|
|
4113
4153
|
const cached = devShellFilesCache.get(devCacheKey);
|
|
4114
|
-
if (
|
|
4115
|
-
cached !== undefined &&
|
|
4116
|
-
sameFileStats(cached.directoryStats, directoryStats)
|
|
4117
|
-
) {
|
|
4154
|
+
if (cached !== undefined && sameFileStats(cached.directoryStats, directoryStats)) {
|
|
4118
4155
|
return cached.files;
|
|
4119
4156
|
}
|
|
4120
4157
|
|
|
@@ -4194,10 +4231,7 @@ async function hasAppMiddleware(options: {
|
|
|
4194
4231
|
if (cacheKey === undefined) {
|
|
4195
4232
|
const directoryStat = await fileStat(options.appDir);
|
|
4196
4233
|
const cached = devAppMiddlewareFileCache.get(options.appDir);
|
|
4197
|
-
if (
|
|
4198
|
-
cached !== undefined &&
|
|
4199
|
-
sameFileStat(cached.directoryStat, directoryStat)
|
|
4200
|
-
) {
|
|
4234
|
+
if (cached !== undefined && sameFileStat(cached.directoryStat, directoryStat)) {
|
|
4201
4235
|
return cached.hasMiddleware;
|
|
4202
4236
|
}
|
|
4203
4237
|
|
|
@@ -4614,6 +4648,7 @@ async function loadRouteMetadata(options: {
|
|
|
4614
4648
|
code: string;
|
|
4615
4649
|
context: RouteMetadataContext;
|
|
4616
4650
|
define?: UserConfig["define"] | undefined;
|
|
4651
|
+
devServerModuleCacheVersion?: string | undefined;
|
|
4617
4652
|
filename: string;
|
|
4618
4653
|
importPolicy?: AppRouterImportPolicy | undefined;
|
|
4619
4654
|
serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
|
|
@@ -4640,22 +4675,85 @@ async function loadRouteMetadata(options: {
|
|
|
4640
4675
|
return await resolveRouteMetadataModule(module, options.context);
|
|
4641
4676
|
}
|
|
4642
4677
|
|
|
4643
|
-
const
|
|
4644
|
-
|
|
4645
|
-
const module = await importAppRouterSourceModule<RouteMetadataModule>({
|
|
4646
|
-
...(options.serverModuleCacheVersion === undefined
|
|
4647
|
-
? {}
|
|
4648
|
-
: {
|
|
4649
|
-
cacheKey: `metadata:${options.filename}:${options.serverModuleCacheVersion}:${memoizedHashText(code)}:${viteDefineCacheKey(options.define)}`,
|
|
4650
|
-
}),
|
|
4651
|
-
code,
|
|
4652
|
-
define: options.define,
|
|
4653
|
-
label: `metadata:${options.filename}`,
|
|
4654
|
-
});
|
|
4678
|
+
const module = await loadRouteMetadataModule(options, prebuiltArtifact);
|
|
4655
4679
|
|
|
4656
4680
|
return await resolveRouteMetadataModule(module, options.context);
|
|
4657
4681
|
}
|
|
4658
4682
|
|
|
4683
|
+
async function loadRouteMetadataModule(
|
|
4684
|
+
options: {
|
|
4685
|
+
appDir: string;
|
|
4686
|
+
code: string;
|
|
4687
|
+
define?: UserConfig["define"] | undefined;
|
|
4688
|
+
devServerModuleCacheVersion?: string | undefined;
|
|
4689
|
+
filename: string;
|
|
4690
|
+
serverModuleCacheVersion?: string | undefined;
|
|
4691
|
+
vitePlugins?: readonly PluginOption[] | undefined;
|
|
4692
|
+
},
|
|
4693
|
+
prebuiltArtifact: BuiltServerModuleArtifact["routeMetadata"] | undefined,
|
|
4694
|
+
): Promise<RouteMetadataModule> {
|
|
4695
|
+
if (prebuiltArtifact?.moduleFile !== undefined) {
|
|
4696
|
+
return await importBuiltServerModuleFile<RouteMetadataModule>({
|
|
4697
|
+
file: prebuiltArtifact.moduleFile,
|
|
4698
|
+
label: `metadata:${options.filename}`,
|
|
4699
|
+
serverModuleCacheVersion: options.serverModuleCacheVersion,
|
|
4700
|
+
});
|
|
4701
|
+
}
|
|
4702
|
+
|
|
4703
|
+
const sourceHash = memoizedHashText(options.code);
|
|
4704
|
+
const moduleCacheVersion =
|
|
4705
|
+
options.serverModuleCacheVersion ?? options.devServerModuleCacheVersion;
|
|
4706
|
+
const moduleCacheKey =
|
|
4707
|
+
moduleCacheVersion === undefined
|
|
4708
|
+
? undefined
|
|
4709
|
+
: `metadata-module:${options.serverModuleCacheVersion === undefined ? "dev" : "build"}:${options.filename}:${moduleCacheVersion}:${sourceHash}:${viteDefineCacheKey(options.define)}:${vitePluginsCacheKey(options.vitePlugins)}`;
|
|
4710
|
+
|
|
4711
|
+
if (moduleCacheKey !== undefined) {
|
|
4712
|
+
const cached = readRouterRuntimeCacheEntry(
|
|
4713
|
+
routeMetadataModuleCache,
|
|
4714
|
+
moduleCacheKey,
|
|
4715
|
+
routeMetadataModuleCacheCounters,
|
|
4716
|
+
);
|
|
4717
|
+
if (cached !== undefined) {
|
|
4718
|
+
return await cached;
|
|
4719
|
+
}
|
|
4720
|
+
}
|
|
4721
|
+
|
|
4722
|
+
const loaded = (async () => {
|
|
4723
|
+
const code = prebuiltArtifact?.code ?? (await bundleRouteMetadataModuleCode(options));
|
|
4724
|
+
|
|
4725
|
+
return await importAppRouterSourceModule<RouteMetadataModule>({
|
|
4726
|
+
...(moduleCacheVersion === undefined
|
|
4727
|
+
? {}
|
|
4728
|
+
: {
|
|
4729
|
+
cacheKey: `metadata:${options.serverModuleCacheVersion === undefined ? "dev" : "build"}:${options.filename}:${moduleCacheVersion}:${memoizedHashText(code)}:${viteDefineCacheKey(options.define)}`,
|
|
4730
|
+
}),
|
|
4731
|
+
code,
|
|
4732
|
+
define: options.define,
|
|
4733
|
+
label: `metadata:${options.filename}`,
|
|
4734
|
+
});
|
|
4735
|
+
})();
|
|
4736
|
+
|
|
4737
|
+
if (moduleCacheKey !== undefined) {
|
|
4738
|
+
setBoundedCacheEntry(
|
|
4739
|
+
routeMetadataModuleCache,
|
|
4740
|
+
moduleCacheKey,
|
|
4741
|
+
loaded,
|
|
4742
|
+
maxRouteMetadataModuleCacheEntries,
|
|
4743
|
+
routeMetadataModuleCacheCounters,
|
|
4744
|
+
);
|
|
4745
|
+
}
|
|
4746
|
+
|
|
4747
|
+
try {
|
|
4748
|
+
return await loaded;
|
|
4749
|
+
} catch (error) {
|
|
4750
|
+
if (moduleCacheKey !== undefined) {
|
|
4751
|
+
routeMetadataModuleCache.delete(moduleCacheKey);
|
|
4752
|
+
}
|
|
4753
|
+
throw error;
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
|
|
4659
4757
|
async function resolveRouteMetadataModule(
|
|
4660
4758
|
module: RouteMetadataModule,
|
|
4661
4759
|
context: RouteMetadataContext,
|
|
@@ -4727,6 +4825,7 @@ async function loadComposedRouteMetadata(options: {
|
|
|
4727
4825
|
code: string;
|
|
4728
4826
|
context: RouteMetadataContext;
|
|
4729
4827
|
define?: UserConfig["define"] | undefined;
|
|
4828
|
+
devServerModuleCacheVersion?: string | undefined;
|
|
4730
4829
|
filename: string;
|
|
4731
4830
|
importPolicy?: AppRouterImportPolicy | undefined;
|
|
4732
4831
|
routes: readonly AppRoute[];
|
|
@@ -4772,14 +4871,18 @@ function composedRouteMetadataCacheKey(options: {
|
|
|
4772
4871
|
appDir: string;
|
|
4773
4872
|
code: string;
|
|
4774
4873
|
define?: UserConfig["define"] | undefined;
|
|
4874
|
+
devServerModuleCacheVersion?: string | undefined;
|
|
4775
4875
|
filename: string;
|
|
4776
4876
|
importPolicy?: AppRouterImportPolicy | undefined;
|
|
4777
4877
|
serverModuleCacheVersion?: string | undefined;
|
|
4778
4878
|
vitePlugins?: readonly PluginOption[] | undefined;
|
|
4779
4879
|
}): string | undefined {
|
|
4780
|
-
|
|
4880
|
+
const moduleCacheVersion =
|
|
4881
|
+
options.serverModuleCacheVersion ?? options.devServerModuleCacheVersion;
|
|
4882
|
+
|
|
4883
|
+
return moduleCacheVersion === undefined
|
|
4781
4884
|
? undefined
|
|
4782
|
-
: `${options.appDir}\0${options.filename}\0${options.serverModuleCacheVersion}\0${memoizedHashText(options.code)}\0${importPolicyCacheKey(options.importPolicy)}\0${viteDefineCacheKey(options.define)}\0${vitePluginsCacheKey(options.vitePlugins)}`;
|
|
4885
|
+
: `${options.appDir}\0${options.filename}\0${options.serverModuleCacheVersion === undefined ? "dev" : "build"}\0${moduleCacheVersion}\0${memoizedHashText(options.code)}\0${importPolicyCacheKey(options.importPolicy)}\0${viteDefineCacheKey(options.define)}\0${vitePluginsCacheKey(options.vitePlugins)}`;
|
|
4783
4886
|
}
|
|
4784
4887
|
|
|
4785
4888
|
export const __composedRouteMetadataCacheKeyForTesting = composedRouteMetadataCacheKey;
|
|
@@ -4789,6 +4892,7 @@ async function loadComposedRouteMetadataUncached(options: {
|
|
|
4789
4892
|
code: string;
|
|
4790
4893
|
context: RouteMetadataContext;
|
|
4791
4894
|
define?: UserConfig["define"] | undefined;
|
|
4895
|
+
devServerModuleCacheVersion?: string | undefined;
|
|
4792
4896
|
filename: string;
|
|
4793
4897
|
importPolicy?: AppRouterImportPolicy | undefined;
|
|
4794
4898
|
routes: readonly AppRoute[];
|
|
@@ -4822,6 +4926,7 @@ async function loadComposedRouteMetadataUncached(options: {
|
|
|
4822
4926
|
importPolicy: options.importPolicy,
|
|
4823
4927
|
serverModules: options.serverModules,
|
|
4824
4928
|
serverModuleCacheVersion: options.serverModuleCacheVersion,
|
|
4929
|
+
devServerModuleCacheVersion: options.devServerModuleCacheVersion,
|
|
4825
4930
|
define: options.define,
|
|
4826
4931
|
vitePlugins: options.vitePlugins,
|
|
4827
4932
|
});
|
|
@@ -4836,6 +4941,7 @@ async function loadComposedRouteMetadataUncached(options: {
|
|
|
4836
4941
|
importPolicy: options.importPolicy,
|
|
4837
4942
|
serverModules: options.serverModules,
|
|
4838
4943
|
serverModuleCacheVersion: options.serverModuleCacheVersion,
|
|
4944
|
+
devServerModuleCacheVersion: options.devServerModuleCacheVersion,
|
|
4839
4945
|
define: options.define,
|
|
4840
4946
|
vitePlugins: options.vitePlugins,
|
|
4841
4947
|
});
|
|
@@ -5083,9 +5189,7 @@ function injectQueryState(html: string, state: DehydratedQueryClient): string {
|
|
|
5083
5189
|
JSON.stringify(state),
|
|
5084
5190
|
)}</script>`;
|
|
5085
5191
|
|
|
5086
|
-
return /<\/body>/i.test(html)
|
|
5087
|
-
? replaceFinalBodyCloseTag(html, script)
|
|
5088
|
-
: `${html}${script}`;
|
|
5192
|
+
return /<\/body>/i.test(html) ? replaceFinalBodyCloseTag(html, script) : `${html}${script}`;
|
|
5089
5193
|
}
|
|
5090
5194
|
|
|
5091
5195
|
function injectAuthSessionClaims(html: string, claims: unknown): string {
|
|
@@ -5097,9 +5201,7 @@ function injectAuthSessionClaims(html: string, claims: unknown): string {
|
|
|
5097
5201
|
JSON.stringify(claims),
|
|
5098
5202
|
)}</script>`;
|
|
5099
5203
|
|
|
5100
|
-
return /<\/body>/i.test(html)
|
|
5101
|
-
? replaceFinalBodyCloseTag(html, script)
|
|
5102
|
-
: `${html}${script}`;
|
|
5204
|
+
return /<\/body>/i.test(html) ? replaceFinalBodyCloseTag(html, script) : `${html}${script}`;
|
|
5103
5205
|
}
|
|
5104
5206
|
|
|
5105
5207
|
function replaceFinalBodyCloseTag(html: string, insertion: string): string {
|
|
@@ -5183,11 +5285,7 @@ function readServerSourceFile(
|
|
|
5183
5285
|
async function readDevServerSourceFile(file: string): Promise<string> {
|
|
5184
5286
|
const stats = await fileStat(file);
|
|
5185
5287
|
const cached = devServerSourceFileCache.get(file);
|
|
5186
|
-
if (
|
|
5187
|
-
cached !== undefined &&
|
|
5188
|
-
cached.mtimeMs === stats.mtimeMs &&
|
|
5189
|
-
cached.size === stats.size
|
|
5190
|
-
) {
|
|
5288
|
+
if (cached !== undefined && cached.mtimeMs === stats.mtimeMs && cached.size === stats.size) {
|
|
5191
5289
|
return cached.source;
|
|
5192
5290
|
}
|
|
5193
5291
|
|
package/src/vite.ts
CHANGED
|
@@ -69,6 +69,7 @@ export interface AppRouterViteMiddlewareOptions extends AppRouterProjectOptions
|
|
|
69
69
|
|
|
70
70
|
type AppRouterViteRuntimeMiddlewareOptions = AppRouterViteMiddlewareOptions & {
|
|
71
71
|
clientRouteInferenceCache?: ClientRouteInferenceCache | undefined;
|
|
72
|
+
devServerModuleCacheVersion?: (() => string | undefined) | undefined;
|
|
72
73
|
navigationScanVitePlugins?: readonly PluginOption[] | undefined;
|
|
73
74
|
viteDevServer?: ViteDevServer | undefined;
|
|
74
75
|
};
|
|
@@ -193,6 +194,7 @@ export function createAppRouterVitePlugin(options: AppRouterVitePluginOptions):
|
|
|
193
194
|
// built-in CSS plugin) whose `transform` requires a dev-server environment the
|
|
194
195
|
// lightweight navigation scan cannot provide. Mirrors what the build forwards.
|
|
195
196
|
let userVitePlugins: readonly PluginOption[] | undefined;
|
|
197
|
+
let devServerModuleCacheVersion = 0;
|
|
196
198
|
|
|
197
199
|
const plugin: MreactRouterPlugin = {
|
|
198
200
|
[mreactRouterConfigKey]: {
|
|
@@ -245,6 +247,7 @@ export function createAppRouterVitePlugin(options: AppRouterVitePluginOptions):
|
|
|
245
247
|
const middlewareOptions: AppRouterViteRuntimeMiddlewareOptions = {
|
|
246
248
|
...options,
|
|
247
249
|
define: server.config.define,
|
|
250
|
+
devServerModuleCacheVersion: () => `dev:${devServerModuleCacheVersion}`,
|
|
248
251
|
navigationScanVitePlugins: userVitePlugins ?? [],
|
|
249
252
|
viteDevServer: server,
|
|
250
253
|
vitePlugins: server.config.plugins,
|
|
@@ -260,6 +263,7 @@ export function createAppRouterVitePlugin(options: AppRouterVitePluginOptions):
|
|
|
260
263
|
return;
|
|
261
264
|
}
|
|
262
265
|
|
|
266
|
+
devServerModuleCacheVersion += 1;
|
|
263
267
|
const timestamp = Date.now();
|
|
264
268
|
const updates = Array.from(context.server.moduleGraph.idToModuleMap.values())
|
|
265
269
|
.filter((moduleNode) => isMreactClientDevModuleId(moduleNode.id))
|
|
@@ -586,6 +590,7 @@ async function handleAppRouterViteRequest(
|
|
|
586
590
|
routeMatcher,
|
|
587
591
|
routes,
|
|
588
592
|
serverActions: options.serverActions,
|
|
593
|
+
devServerModuleCacheVersion: options.devServerModuleCacheVersion?.(),
|
|
589
594
|
vitePlugins: routeTransformPlugins,
|
|
590
595
|
}),
|
|
591
596
|
);
|
|
@@ -907,9 +912,7 @@ async function devNavigationScripts(
|
|
|
907
912
|
console.warn(formatClientRouteInferenceDiagnostic(navigationRuntimeDiagnostic));
|
|
908
913
|
}
|
|
909
914
|
|
|
910
|
-
return navigation
|
|
911
|
-
? ([route.path, navigationRuntimeScriptForDev()] as const)
|
|
912
|
-
: undefined;
|
|
915
|
+
return navigation ? ([route.path, navigationRuntimeScriptForDev()] as const) : undefined;
|
|
913
916
|
}),
|
|
914
917
|
);
|
|
915
918
|
|