@reckona/mreact-router 0.0.207 → 0.0.209
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 +2 -2
- package/dist/adapters/cloudflare.d.ts +3 -3
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +11 -4
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/boundaries.d.ts +37 -1
- package/dist/boundaries.d.ts.map +1 -1
- package/dist/boundaries.js +125 -4
- package/dist/boundaries.js.map +1 -1
- package/dist/build.d.ts +6 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +269 -24
- package/dist/build.js.map +1 -1
- package/dist/bundle-pipeline.d.ts +1 -0
- package/dist/bundle-pipeline.d.ts.map +1 -1
- package/dist/bundle-pipeline.js +4 -1
- package/dist/bundle-pipeline.js.map +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +67 -7
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +7 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +17 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +5 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/module-runner.js +9 -1
- package/dist/module-runner.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +23 -23
- package/dist/render.js.map +1 -1
- package/dist/request-header-tracking.d.ts +19 -0
- package/dist/request-header-tracking.d.ts.map +1 -1
- package/dist/request-header-tracking.js +48 -0
- package/dist/request-header-tracking.js.map +1 -1
- package/dist/tailwind-source.d.ts.map +1 -1
- package/dist/tailwind-source.js +147 -1
- package/dist/tailwind-source.js.map +1 -1
- package/dist/types.d.ts +14 -2
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/vite.d.ts +1 -0
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +4 -0
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/adapters/cloudflare.ts +14 -5
- package/src/boundaries.ts +224 -4
- package/src/build.ts +383 -24
- package/src/bundle-pipeline.ts +9 -4
- package/src/client.ts +104 -10
- package/src/config.ts +32 -0
- package/src/index.ts +10 -0
- package/src/module-runner.ts +9 -1
- package/src/render.ts +30 -16
- package/src/request-header-tracking.ts +61 -0
- package/src/tailwind-source.ts +164 -1
- package/src/types.ts +15 -2
- package/src/vite.ts +5 -0
package/src/client.ts
CHANGED
|
@@ -53,6 +53,7 @@ const nodeBuiltinPackages = new Set(builtinModules.flatMap((name) => [name, `nod
|
|
|
53
53
|
export interface ClientRouteManifestEntry {
|
|
54
54
|
bytes?: number;
|
|
55
55
|
css?: readonly string[];
|
|
56
|
+
dynamicImports?: readonly string[];
|
|
56
57
|
path: string;
|
|
57
58
|
kind: AppRoute["kind"];
|
|
58
59
|
client: boolean;
|
|
@@ -842,14 +843,14 @@ function clientRouteComponentOrigin(options: {
|
|
|
842
843
|
return "server-only-import";
|
|
843
844
|
}
|
|
844
845
|
|
|
845
|
-
if (options.analysis.hasUseClientDirective) {
|
|
846
|
-
return "use-client-directive";
|
|
847
|
-
}
|
|
848
|
-
|
|
849
846
|
if (/\.compat(?:\.mreact)?\.[cm]?[jt]sx?$/.test(options.filename)) {
|
|
850
847
|
return "compat-filename";
|
|
851
848
|
}
|
|
852
849
|
|
|
850
|
+
if (options.analysis.hasUseClientDirective) {
|
|
851
|
+
return "use-client-directive";
|
|
852
|
+
}
|
|
853
|
+
|
|
853
854
|
if (/\.client(?:\.mreact)?\.[cm]?[jt]sx?$/.test(options.filename)) {
|
|
854
855
|
return "client-filename";
|
|
855
856
|
}
|
|
@@ -1853,6 +1854,11 @@ function isClientBoundaryFallbackEligibleSource(source: string, filename?: strin
|
|
|
1853
1854
|
}
|
|
1854
1855
|
}
|
|
1855
1856
|
|
|
1857
|
+
sourceWithoutGuardedUndefinedCallbacks = removeSelfContainedIntrinsicHandlerAttributes(
|
|
1858
|
+
sourceWithoutGuardedUndefinedCallbacks,
|
|
1859
|
+
callbackPropNames,
|
|
1860
|
+
);
|
|
1861
|
+
|
|
1856
1862
|
return (
|
|
1857
1863
|
!/\bon[A-Z][A-Za-z0-9_$]*\s*=/u.test(sourceWithoutGuardedUndefinedCallbacks) &&
|
|
1858
1864
|
!/\bglobalThis\b/u.test(source)
|
|
@@ -1896,6 +1902,20 @@ function propsCallbackAliasNames(source: string): Set<string> {
|
|
|
1896
1902
|
}
|
|
1897
1903
|
}
|
|
1898
1904
|
|
|
1905
|
+
for (const match of source.matchAll(
|
|
1906
|
+
/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*props\s*\[\s*["']([A-Za-z_$][\w$]*)["']\s*\]/gu,
|
|
1907
|
+
)) {
|
|
1908
|
+
const localName = match[1];
|
|
1909
|
+
const propName = match[2];
|
|
1910
|
+
|
|
1911
|
+
if (
|
|
1912
|
+
localName !== undefined &&
|
|
1913
|
+
(isCallbackPropName(propName) || isCallbackPropName(localName))
|
|
1914
|
+
) {
|
|
1915
|
+
names.add(localName);
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1899
1919
|
for (const match of source.matchAll(
|
|
1900
1920
|
/\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*\(\s*props\s+as\s+[^)]+\)\.([A-Za-z_$][\w$]*)\b/gu,
|
|
1901
1921
|
)) {
|
|
@@ -1978,6 +1998,14 @@ function memberCallbackNames(source: string): Set<string> {
|
|
|
1978
1998
|
}
|
|
1979
1999
|
}
|
|
1980
2000
|
|
|
2001
|
+
for (const match of source.matchAll(/\[\s*["']([A-Za-z_$][\w$]*)["']\s*\]/gu)) {
|
|
2002
|
+
const memberName = match[1];
|
|
2003
|
+
|
|
2004
|
+
if (memberName !== undefined && isCallbackPropName(memberName)) {
|
|
2005
|
+
names.add(memberName);
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
2008
|
+
|
|
1981
2009
|
return names;
|
|
1982
2010
|
}
|
|
1983
2011
|
|
|
@@ -2009,6 +2037,56 @@ function removeSafeCallbackHandlerAttributes(
|
|
|
2009
2037
|
return cursor === 0 ? source : result + source.slice(cursor);
|
|
2010
2038
|
}
|
|
2011
2039
|
|
|
2040
|
+
function removeSelfContainedIntrinsicHandlerAttributes(
|
|
2041
|
+
source: string,
|
|
2042
|
+
callbackNames: ReadonlySet<string>,
|
|
2043
|
+
): string {
|
|
2044
|
+
const attributePattern = /\bon[A-Z][A-Za-z0-9_$]*\s*=\s*\{/gu;
|
|
2045
|
+
let result = "";
|
|
2046
|
+
let cursor = 0;
|
|
2047
|
+
|
|
2048
|
+
for (const match of source.matchAll(attributePattern)) {
|
|
2049
|
+
const start = match.index;
|
|
2050
|
+
const expressionStart = start + match[0].length;
|
|
2051
|
+
const end = matchingBraceEnd(source, expressionStart - 1);
|
|
2052
|
+
|
|
2053
|
+
if (end === undefined || !isIntrinsicJsxAttribute(source, start)) {
|
|
2054
|
+
continue;
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
const expression = source.slice(expressionStart, end);
|
|
2058
|
+
if (
|
|
2059
|
+
!isInlineFunctionExpression(expression) ||
|
|
2060
|
+
Array.from(callbackNames).some((name) =>
|
|
2061
|
+
new RegExp(String.raw`\b${escapeRegExp(name)}\b`, "u").test(expression),
|
|
2062
|
+
)
|
|
2063
|
+
) {
|
|
2064
|
+
continue;
|
|
2065
|
+
}
|
|
2066
|
+
|
|
2067
|
+
result += source.slice(cursor, start);
|
|
2068
|
+
cursor = end + 1;
|
|
2069
|
+
}
|
|
2070
|
+
|
|
2071
|
+
return cursor === 0 ? source : result + source.slice(cursor);
|
|
2072
|
+
}
|
|
2073
|
+
|
|
2074
|
+
function isIntrinsicJsxAttribute(source: string, attributeStart: number): boolean {
|
|
2075
|
+
const tagStart = source.lastIndexOf("<", attributeStart);
|
|
2076
|
+
if (tagStart === -1 || source.lastIndexOf(">", attributeStart) > tagStart) {
|
|
2077
|
+
return false;
|
|
2078
|
+
}
|
|
2079
|
+
|
|
2080
|
+
return /^<\s*[a-z][A-Za-z0-9:_-]*(?:\s|$)/u.test(source.slice(tagStart, attributeStart));
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
function isInlineFunctionExpression(expression: string): boolean {
|
|
2084
|
+
return (
|
|
2085
|
+
/^\s*(?:async\s+)?(?:\([^)]*\)|[A-Za-z_$][\w$]*)\s*=>/u.test(expression) ||
|
|
2086
|
+
/^\s*(?:async\s+)?function(?:\s+[A-Za-z_$][\w$]*)?\s*\(/u.test(expression)
|
|
2087
|
+
);
|
|
2088
|
+
}
|
|
2089
|
+
|
|
2012
2090
|
function matchingBraceEnd(source: string, openBraceIndex: number): number | undefined {
|
|
2013
2091
|
let depth = 0;
|
|
2014
2092
|
let quote: '"' | "'" | "`" | undefined;
|
|
@@ -3372,10 +3450,10 @@ function __mreactResolveRouteNode(value) {
|
|
|
3372
3450
|
`
|
|
3373
3451
|
: `function __mreactSyncDomRefBindings() {}
|
|
3374
3452
|
`;
|
|
3375
|
-
const boundaryOnlyHydrationBlock =
|
|
3376
|
-
|| clientReferenceManifest.length === 0
|
|
3377
|
-
|
|
3378
|
-
|
|
3453
|
+
const boundaryOnlyHydrationBlock =
|
|
3454
|
+
routeRequiresFullHydration || clientReferenceManifest.length === 0
|
|
3455
|
+
? ""
|
|
3456
|
+
: `${routeCellHydrationIndent}if (!__mreactHasNonSerializableClientBoundaries(__mreactMarker) && __mreactHydrateClientBoundaries(document, __mreactClientReferences, __mreactClientReferenceComponents)) {
|
|
3379
3457
|
${routeCellHydrationIndent} __mreactMarker.setAttribute(${JSON.stringify(routeHydrationContract.hydratedAttribute)}, "true");
|
|
3380
3458
|
${routeCellHydrationIndent} __mreactMarkRouteHydrated();
|
|
3381
3459
|
${routeCellHydrationIndent} return;
|
|
@@ -3414,7 +3492,18 @@ ${routeOutOfOrderFragmentHydration} const __mreactMarker = document.querySelect
|
|
|
3414
3492
|
const __mreactProps = __mreactPropsText === undefined
|
|
3415
3493
|
? {}
|
|
3416
3494
|
: JSON.parse(__mreactPropsText);
|
|
3417
|
-
${
|
|
3495
|
+
${
|
|
3496
|
+
restoreRequestUrl
|
|
3497
|
+
? ` if (__mreactProps.request) {
|
|
3498
|
+
const __mreactRouteUrl = new URL(document.URL);
|
|
3499
|
+
__mreactProps.request.url = __mreactRouteUrl.href;
|
|
3500
|
+
__mreactProps.request.pathname = __mreactRouteUrl.pathname;
|
|
3501
|
+
__mreactProps.request.search = __mreactRouteUrl.search;
|
|
3502
|
+
__mreactProps.request.hash = __mreactRouteUrl.hash;
|
|
3503
|
+
}
|
|
3504
|
+
`
|
|
3505
|
+
: ""
|
|
3506
|
+
}
|
|
3418
3507
|
const __mreactClientReferences = __mreactClientReferencesElement?.textContent === undefined
|
|
3419
3508
|
? []
|
|
3420
3509
|
: JSON.parse(__mreactClientReferencesElement.textContent);
|
|
@@ -5400,6 +5489,10 @@ function workspaceRuntimePlugin(options: {
|
|
|
5400
5489
|
"@reckona/mreact-compat/server",
|
|
5401
5490
|
packageFile("react-compat", "@reckona/mreact-compat", "server"),
|
|
5402
5491
|
],
|
|
5492
|
+
[
|
|
5493
|
+
"@reckona/mreact-shared/server-render-value-internal",
|
|
5494
|
+
packageFile("shared", "@reckona/mreact-shared", "server-render-value-internal"),
|
|
5495
|
+
],
|
|
5403
5496
|
["@reckona/mreact-reactive-dom", reactiveDomPath],
|
|
5404
5497
|
]);
|
|
5405
5498
|
|
|
@@ -5428,7 +5521,7 @@ function workspaceRuntimePlugin(options: {
|
|
|
5428
5521
|
buildApi.onResolve(
|
|
5429
5522
|
{
|
|
5430
5523
|
filter:
|
|
5431
|
-
/^(?:react(?:\/jsx-(?:dev-)?runtime)?|react-dom(?:\/(?:client|server))?|@reckona\/mreact-(?:compat|reactive-core|reactive-dom)(?:\/(?:event-priority|flight|internal|jsx-dev-runtime|jsx-runtime|scheduler))
|
|
5524
|
+
/^(?:react(?:\/jsx-(?:dev-)?runtime)?|react-dom(?:\/(?:client|server))?|@reckona\/mreact-(?:compat|reactive-core|reactive-dom)(?:\/(?:event-priority|flight|internal|jsx-dev-runtime|jsx-runtime|scheduler))?|@reckona\/mreact-shared\/server-render-value-internal)$/,
|
|
5432
5525
|
},
|
|
5433
5526
|
(args) => {
|
|
5434
5527
|
const path = runtimePaths.get(args.path);
|
|
@@ -5452,6 +5545,7 @@ export function emitReactiveEffectRunDevtoolsEvent() {}
|
|
|
5452
5545
|
export function hasReactiveDevtoolsEmitter() { return false; }
|
|
5453
5546
|
export function currentDevtoolsEmitter() { return undefined; }
|
|
5454
5547
|
export function currentReactiveDevtools() { return undefined; }
|
|
5548
|
+
export function registerReactiveDevtoolsResource() { return { dispose() {}, update() {} }; }
|
|
5455
5549
|
export function invalidateReactiveDevtoolsCache() {}
|
|
5456
5550
|
export function prepareReactiveEffectRunDevtoolsEvent() { return undefined; }`,
|
|
5457
5551
|
loader: "ts",
|
package/src/config.ts
CHANGED
|
@@ -32,6 +32,12 @@ export interface AppRouterProductionOptions {
|
|
|
32
32
|
dropClientConsole?: boolean | readonly AppRouterClientConsoleMethod[] | undefined;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
/** Opt-in compile-time constraints for route and component execution modes. */
|
|
36
|
+
export interface AppRouterExecutionContracts {
|
|
37
|
+
noCompatComponents?: readonly string[] | undefined;
|
|
38
|
+
serverOnlyRoutes?: readonly string[] | undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
35
41
|
export interface AppRouterProjectOptions {
|
|
36
42
|
assetBaseUrl?: string | undefined;
|
|
37
43
|
buildConcurrency?: number | undefined;
|
|
@@ -39,6 +45,7 @@ export interface AppRouterProjectOptions {
|
|
|
39
45
|
clientSourceMaps?: AppRouterClientSourceMapOption | undefined;
|
|
40
46
|
/** Project-local server module exporting a named `dehydrateOptions` policy. */
|
|
41
47
|
dehydratePolicyModule?: string | undefined;
|
|
48
|
+
executionContracts?: AppRouterExecutionContracts | undefined;
|
|
42
49
|
/**
|
|
43
50
|
* Legacy route root. When provided without routesDir/projectRoot, this keeps
|
|
44
51
|
* the historical "appDir is the whole app boundary" behavior.
|
|
@@ -64,6 +71,7 @@ export interface ResolvedAppRouterProject {
|
|
|
64
71
|
clientSourceMaps: AppRouterClientSourceMapMode;
|
|
65
72
|
clientConsolePureFunctions?: readonly string[] | undefined;
|
|
66
73
|
dehydratePolicyModule?: string | undefined;
|
|
74
|
+
executionContracts?: AppRouterExecutionContracts | undefined;
|
|
67
75
|
projectRoot: string;
|
|
68
76
|
publicAssetBaseUrl?: string | undefined;
|
|
69
77
|
publicDir: string;
|
|
@@ -105,6 +113,9 @@ export function resolveAppRouterProjectOptions(
|
|
|
105
113
|
allowedSourceDirs,
|
|
106
114
|
),
|
|
107
115
|
}),
|
|
116
|
+
...(options.executionContracts === undefined
|
|
117
|
+
? {}
|
|
118
|
+
: { executionContracts: normalizeExecutionContracts(options.executionContracts) }),
|
|
108
119
|
projectRoot: appDir,
|
|
109
120
|
...(options.publicAssetBaseUrl === undefined
|
|
110
121
|
? {}
|
|
@@ -140,6 +151,9 @@ export function resolveAppRouterProjectOptions(
|
|
|
140
151
|
allowedSourceDirs,
|
|
141
152
|
),
|
|
142
153
|
}),
|
|
154
|
+
...(options.executionContracts === undefined
|
|
155
|
+
? {}
|
|
156
|
+
: { executionContracts: normalizeExecutionContracts(options.executionContracts) }),
|
|
143
157
|
projectRoot,
|
|
144
158
|
...(options.publicAssetBaseUrl === undefined
|
|
145
159
|
? {}
|
|
@@ -149,6 +163,24 @@ export function resolveAppRouterProjectOptions(
|
|
|
149
163
|
};
|
|
150
164
|
}
|
|
151
165
|
|
|
166
|
+
function normalizeExecutionContracts(
|
|
167
|
+
contracts: AppRouterExecutionContracts,
|
|
168
|
+
): AppRouterExecutionContracts {
|
|
169
|
+
const noCompatComponents = uniquePatterns(contracts.noCompatComponents);
|
|
170
|
+
const serverOnlyRoutes = uniquePatterns(contracts.serverOnlyRoutes);
|
|
171
|
+
|
|
172
|
+
return {
|
|
173
|
+
...(noCompatComponents.length === 0 ? {} : { noCompatComponents }),
|
|
174
|
+
...(serverOnlyRoutes.length === 0 ? {} : { serverOnlyRoutes }),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function uniquePatterns(patterns: readonly string[] | undefined): string[] {
|
|
179
|
+
return [...new Set((patterns ?? []).filter((pattern) => pattern.length > 0))].sort(
|
|
180
|
+
(left, right) => left.localeCompare(right),
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
152
184
|
function resolvePolicyModule(
|
|
153
185
|
projectRoot: string,
|
|
154
186
|
modulePath: string,
|
package/src/index.ts
CHANGED
|
@@ -5,13 +5,20 @@ export {
|
|
|
5
5
|
createBoundaryReport,
|
|
6
6
|
formatBoundaryReport,
|
|
7
7
|
formatBoundaryReportJson,
|
|
8
|
+
validateBoundaryExecutionContracts,
|
|
8
9
|
} from "./boundaries.js";
|
|
9
10
|
export type {
|
|
10
11
|
AnalyzeAppBoundariesOptions,
|
|
12
|
+
BoundaryExecutionMode,
|
|
13
|
+
BoundaryReportByteCost,
|
|
11
14
|
BoundaryReport,
|
|
12
15
|
BoundaryReportComponent,
|
|
16
|
+
BoundaryReportCost,
|
|
17
|
+
BoundaryReportDecision,
|
|
13
18
|
BoundaryReportRoute,
|
|
14
19
|
BoundaryReportSummary,
|
|
20
|
+
BoundarySourcePosition,
|
|
21
|
+
BoundarySourceRange,
|
|
15
22
|
CreateBoundaryReportInput,
|
|
16
23
|
CreateBoundaryReportRouteInput,
|
|
17
24
|
} from "./boundaries.js";
|
|
@@ -142,6 +149,7 @@ export async function rotateSession<TData>(
|
|
|
142
149
|
export type {
|
|
143
150
|
AwsLambdaArtifactManifest,
|
|
144
151
|
AwsLambdaGeneratedHandlerPreloadMode,
|
|
152
|
+
BoundaryCostOptions,
|
|
145
153
|
BuildAppPhase,
|
|
146
154
|
BuildAppPhaseTiming,
|
|
147
155
|
BuildAppOptions,
|
|
@@ -195,6 +203,7 @@ export type {
|
|
|
195
203
|
RobotsRule,
|
|
196
204
|
RouteHeadDescriptor,
|
|
197
205
|
RouteHandlerContext,
|
|
206
|
+
RouteLocation,
|
|
198
207
|
RouteLoader,
|
|
199
208
|
RouteMetadata,
|
|
200
209
|
RouteParams,
|
|
@@ -208,6 +217,7 @@ export type {
|
|
|
208
217
|
AppRouterClientConsoleMethod,
|
|
209
218
|
AppRouterClientSourceMapMode,
|
|
210
219
|
AppRouterClientSourceMapOption,
|
|
220
|
+
AppRouterExecutionContracts,
|
|
211
221
|
AppRouterProductionOptions,
|
|
212
222
|
} from "./config.js";
|
|
213
223
|
export type {
|
package/src/module-runner.ts
CHANGED
|
@@ -1381,6 +1381,14 @@ function workspacePackageResolutionPlugin() {
|
|
|
1381
1381
|
"@reckona/mreact-server",
|
|
1382
1382
|
{ entry: "index", monorepoDir: "server", packageName: "@reckona/mreact-server" },
|
|
1383
1383
|
],
|
|
1384
|
+
[
|
|
1385
|
+
"@reckona/mreact-shared/server-render-value-internal",
|
|
1386
|
+
{
|
|
1387
|
+
entry: "server-render-value-internal",
|
|
1388
|
+
monorepoDir: "shared",
|
|
1389
|
+
packageName: "@reckona/mreact-shared",
|
|
1390
|
+
},
|
|
1391
|
+
],
|
|
1384
1392
|
]);
|
|
1385
1393
|
const routerEntries = new Map([
|
|
1386
1394
|
["@reckona/mreact-router", join(packageRoot, sourceOrDist)],
|
|
@@ -1400,7 +1408,7 @@ function workspacePackageResolutionPlugin() {
|
|
|
1400
1408
|
buildApi.onResolve(
|
|
1401
1409
|
{
|
|
1402
1410
|
filter:
|
|
1403
|
-
/^(?:react(?:\/jsx-(?:dev-)?runtime)?|react-dom(?:\/(?:client|server))?|@reckona\/(?:mreact(?:\/(?:jsx-dev-runtime|jsx-runtime))?|mreact-(?:auth|query|reactive-core|server|router|compat)(?:\/(?:event-priority|flight|internal|jsx-dev-runtime|jsx-runtime|scheduler|app-router-globals|link|native-escape|navigation-state|session|stream-list|internal\/native-escape|internal\/session))?))$/,
|
|
1411
|
+
/^(?:react(?:\/jsx-(?:dev-)?runtime)?|react-dom(?:\/(?:client|server))?|@reckona\/(?:mreact(?:\/(?:jsx-dev-runtime|jsx-runtime))?|mreact-shared\/server-render-value-internal|mreact-(?:auth|query|reactive-core|server|router|compat)(?:\/(?:event-priority|flight|internal|jsx-dev-runtime|jsx-runtime|scheduler|app-router-globals|link|native-escape|navigation-state|session|stream-list|internal\/native-escape|internal\/session))?))$/,
|
|
1404
1412
|
},
|
|
1405
1413
|
(args) => {
|
|
1406
1414
|
const routerPath = routerEntries.get(args.path);
|
package/src/render.ts
CHANGED
|
@@ -64,7 +64,11 @@ import {
|
|
|
64
64
|
} from "./cache.js";
|
|
65
65
|
import {
|
|
66
66
|
trackRequestHeaderReads,
|
|
67
|
+
routeLocationFromRequest,
|
|
68
|
+
routeLocationForHydration,
|
|
69
|
+
routeLocationFromUrl,
|
|
67
70
|
type TrackedHeaderRequest,
|
|
71
|
+
withTrackedRouteLocation,
|
|
68
72
|
withTrackedRequest,
|
|
69
73
|
} from "./request-header-tracking.js";
|
|
70
74
|
import { configuredHstsHeader, hasInvalidConfiguredHsts } from "./security-headers.js";
|
|
@@ -108,6 +112,7 @@ import { viteDefineCacheKey, vitePluginsCacheKey } from "./vite-plugin-cache-key
|
|
|
108
112
|
import type {
|
|
109
113
|
ManifestDescriptor,
|
|
110
114
|
RobotsManifest,
|
|
115
|
+
RouteLocation,
|
|
111
116
|
RouteParams,
|
|
112
117
|
RouteMetadata,
|
|
113
118
|
SitemapEntry,
|
|
@@ -543,7 +548,7 @@ interface ServerComponentProps {
|
|
|
543
548
|
data: unknown;
|
|
544
549
|
params: RouteParams;
|
|
545
550
|
queryClient: QueryClient;
|
|
546
|
-
request:
|
|
551
|
+
request: RouteLocation;
|
|
547
552
|
}
|
|
548
553
|
|
|
549
554
|
type ServerComponent = (props: ServerComponentProps) => string | PromiseLike<string>;
|
|
@@ -1185,7 +1190,7 @@ async function renderAppRequestInternal(
|
|
|
1185
1190
|
clientRoute,
|
|
1186
1191
|
props: {
|
|
1187
1192
|
params: matched.params,
|
|
1188
|
-
request:
|
|
1193
|
+
request: routeLocationForHydration(url),
|
|
1189
1194
|
},
|
|
1190
1195
|
routePath: matched.route.path,
|
|
1191
1196
|
script: clientScript,
|
|
@@ -1252,7 +1257,7 @@ async function renderAppRequestInternal(
|
|
|
1252
1257
|
const renderedPage = await runWithQueryClient(queryClient, () =>
|
|
1253
1258
|
runServerModuleWithSlots(
|
|
1254
1259
|
stringOutput.code,
|
|
1255
|
-
|
|
1260
|
+
withTrackedRouteLocation(
|
|
1256
1261
|
{
|
|
1257
1262
|
data,
|
|
1258
1263
|
params: matched.params,
|
|
@@ -1260,6 +1265,7 @@ async function renderAppRequestInternal(
|
|
|
1260
1265
|
},
|
|
1261
1266
|
appRequest,
|
|
1262
1267
|
trackedRequest,
|
|
1268
|
+
routeLocationFromUrl(url),
|
|
1263
1269
|
),
|
|
1264
1270
|
matched.route.file,
|
|
1265
1271
|
options.serverModules,
|
|
@@ -1281,7 +1287,7 @@ async function renderAppRequestInternal(
|
|
|
1281
1287
|
script: clientScript,
|
|
1282
1288
|
props: {
|
|
1283
1289
|
params: matched.params,
|
|
1284
|
-
request:
|
|
1290
|
+
request: routeLocationForHydration(url),
|
|
1285
1291
|
data,
|
|
1286
1292
|
},
|
|
1287
1293
|
})
|
|
@@ -1296,7 +1302,7 @@ async function renderAppRequestInternal(
|
|
|
1296
1302
|
appDir: options.appDir,
|
|
1297
1303
|
pageFile: matched.route.file,
|
|
1298
1304
|
html: pageHtmlForLayout,
|
|
1299
|
-
props:
|
|
1305
|
+
props: withTrackedRouteLocation(
|
|
1300
1306
|
{
|
|
1301
1307
|
data,
|
|
1302
1308
|
params: matched.params,
|
|
@@ -1304,6 +1310,7 @@ async function renderAppRequestInternal(
|
|
|
1304
1310
|
},
|
|
1305
1311
|
appRequest,
|
|
1306
1312
|
trackedRequest,
|
|
1313
|
+
routeLocationFromUrl(url),
|
|
1307
1314
|
),
|
|
1308
1315
|
slots: renderedPage.slots,
|
|
1309
1316
|
serverModules: options.serverModules,
|
|
@@ -1437,7 +1444,7 @@ async function renderAppRequestInternal(
|
|
|
1437
1444
|
params: matched.params,
|
|
1438
1445
|
queryClient,
|
|
1439
1446
|
request: appRequest,
|
|
1440
|
-
requestUrl: url.
|
|
1447
|
+
requestUrl: url.href,
|
|
1441
1448
|
trackedRequest,
|
|
1442
1449
|
routePath: matched.route.path,
|
|
1443
1450
|
routeScripts: options.clientScripts,
|
|
@@ -1470,7 +1477,7 @@ async function renderAppRequestInternal(
|
|
|
1470
1477
|
}
|
|
1471
1478
|
|
|
1472
1479
|
const data = streamData;
|
|
1473
|
-
const props =
|
|
1480
|
+
const props = withTrackedRouteLocation(
|
|
1474
1481
|
{
|
|
1475
1482
|
data,
|
|
1476
1483
|
params: matched.params,
|
|
@@ -1478,6 +1485,7 @@ async function renderAppRequestInternal(
|
|
|
1478
1485
|
},
|
|
1479
1486
|
appRequest,
|
|
1480
1487
|
trackedRequest,
|
|
1488
|
+
routeLocationFromUrl(url),
|
|
1481
1489
|
);
|
|
1482
1490
|
phaseStartedAt = renderTimingPhaseStartedAt(timing);
|
|
1483
1491
|
const stream = runServerStreamModule(output.code, {
|
|
@@ -1487,7 +1495,7 @@ async function renderAppRequestInternal(
|
|
|
1487
1495
|
markerRequest: options.request,
|
|
1488
1496
|
prerenderVariantCapture: options.prerenderVariantCapture,
|
|
1489
1497
|
props,
|
|
1490
|
-
requestUrl: url.
|
|
1498
|
+
requestUrl: url.href,
|
|
1491
1499
|
routePath: matched.route.path,
|
|
1492
1500
|
routeScripts: options.clientScripts,
|
|
1493
1501
|
serverModules: options.serverModules,
|
|
@@ -1557,7 +1565,7 @@ async function renderAppRequestInternal(
|
|
|
1557
1565
|
const renderedPage = await runWithQueryClient(queryClient, () =>
|
|
1558
1566
|
runServerModuleWithSlots(
|
|
1559
1567
|
output.code,
|
|
1560
|
-
|
|
1568
|
+
withTrackedRouteLocation(
|
|
1561
1569
|
{
|
|
1562
1570
|
data,
|
|
1563
1571
|
params: matched.params,
|
|
@@ -1565,6 +1573,7 @@ async function renderAppRequestInternal(
|
|
|
1565
1573
|
},
|
|
1566
1574
|
appRequest,
|
|
1567
1575
|
trackedRequest,
|
|
1576
|
+
routeLocationFromUrl(url),
|
|
1568
1577
|
),
|
|
1569
1578
|
matched.route.file,
|
|
1570
1579
|
options.serverModules,
|
|
@@ -1592,7 +1601,7 @@ async function renderAppRequestInternal(
|
|
|
1592
1601
|
script: clientScript,
|
|
1593
1602
|
props: {
|
|
1594
1603
|
params: matched.params,
|
|
1595
|
-
request:
|
|
1604
|
+
request: routeLocationForHydration(url),
|
|
1596
1605
|
data,
|
|
1597
1606
|
},
|
|
1598
1607
|
})
|
|
@@ -1608,7 +1617,7 @@ async function renderAppRequestInternal(
|
|
|
1608
1617
|
appDir: options.appDir,
|
|
1609
1618
|
pageFile: matched.route.file,
|
|
1610
1619
|
html: pageHtmlForLayout,
|
|
1611
|
-
props:
|
|
1620
|
+
props: withTrackedRouteLocation(
|
|
1612
1621
|
{
|
|
1613
1622
|
data,
|
|
1614
1623
|
params: matched.params,
|
|
@@ -1616,6 +1625,7 @@ async function renderAppRequestInternal(
|
|
|
1616
1625
|
},
|
|
1617
1626
|
appRequest,
|
|
1618
1627
|
trackedRequest,
|
|
1628
|
+
routeLocationFromUrl(url),
|
|
1619
1629
|
),
|
|
1620
1630
|
slots: renderedPage.slots,
|
|
1621
1631
|
serverModules: options.serverModules,
|
|
@@ -2300,7 +2310,7 @@ function errorBoundaryProps(error: unknown, request: Request, routePath: string
|
|
|
2300
2310
|
error: normalizeErrorForProps(error),
|
|
2301
2311
|
params: {},
|
|
2302
2312
|
queryClient: createQueryClient(),
|
|
2303
|
-
request,
|
|
2313
|
+
request: routeLocationFromRequest(request),
|
|
2304
2314
|
requestId: requestIdForErrorContext(request),
|
|
2305
2315
|
routeId: routeIdForPath(routePath ?? new URL(request.url).pathname),
|
|
2306
2316
|
traceId: traceContextFromRequest(request)?.traceId,
|
|
@@ -3550,7 +3560,9 @@ function runServerStreamModule(
|
|
|
3550
3560
|
script: options.script,
|
|
3551
3561
|
props: {
|
|
3552
3562
|
params: options.props.params,
|
|
3553
|
-
request:
|
|
3563
|
+
request: routeLocationForHydration(
|
|
3564
|
+
new URL(options.requestUrl, options.markerRequest.url),
|
|
3565
|
+
),
|
|
3554
3566
|
data: options.props.data,
|
|
3555
3567
|
},
|
|
3556
3568
|
})
|
|
@@ -3797,7 +3809,7 @@ async function runServerStreamModuleWithLoading(
|
|
|
3797
3809
|
importPolicy?: AppRouterImportPolicy | undefined;
|
|
3798
3810
|
},
|
|
3799
3811
|
): Promise<ReadableStream<Uint8Array> | Response> {
|
|
3800
|
-
const loadingProps =
|
|
3812
|
+
const loadingProps = withTrackedRouteLocation(
|
|
3801
3813
|
{
|
|
3802
3814
|
data: undefined,
|
|
3803
3815
|
params: options.params,
|
|
@@ -3805,6 +3817,7 @@ async function runServerStreamModuleWithLoading(
|
|
|
3805
3817
|
},
|
|
3806
3818
|
options.request,
|
|
3807
3819
|
options.trackedRequest,
|
|
3820
|
+
routeLocationFromUrl(new URL(options.requestUrl)),
|
|
3808
3821
|
);
|
|
3809
3822
|
const layoutShells = await layoutShellsForPage(
|
|
3810
3823
|
options.appDir,
|
|
@@ -3836,7 +3849,7 @@ async function runServerStreamModuleWithLoading(
|
|
|
3836
3849
|
script: options.script,
|
|
3837
3850
|
props: {
|
|
3838
3851
|
params: options.params,
|
|
3839
|
-
request:
|
|
3852
|
+
request: routeLocationForHydration(new URL(options.requestUrl)),
|
|
3840
3853
|
},
|
|
3841
3854
|
})
|
|
3842
3855
|
: serverOnlyRouteMarkerParts(
|
|
@@ -3879,7 +3892,7 @@ async function runServerStreamModuleWithLoading(
|
|
|
3879
3892
|
await appendServerStreamModule(
|
|
3880
3893
|
code,
|
|
3881
3894
|
boundarySink,
|
|
3882
|
-
|
|
3895
|
+
withTrackedRouteLocation(
|
|
3883
3896
|
{
|
|
3884
3897
|
data,
|
|
3885
3898
|
params: options.params,
|
|
@@ -3887,6 +3900,7 @@ async function runServerStreamModuleWithLoading(
|
|
|
3887
3900
|
},
|
|
3888
3901
|
options.request,
|
|
3889
3902
|
options.trackedRequest,
|
|
3903
|
+
routeLocationFromUrl(new URL(options.requestUrl)),
|
|
3890
3904
|
),
|
|
3891
3905
|
options.pageFile,
|
|
3892
3906
|
options.serverModules,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { RouteLocation } from "./types.js";
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Observes request inputs read by application code during a render.
|
|
3
5
|
*
|
|
@@ -159,3 +161,62 @@ export function withTrackedRequest<T extends object>(
|
|
|
159
161
|
|
|
160
162
|
return context;
|
|
161
163
|
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Adds the serializable location view used by shared page and layout props.
|
|
167
|
+
* The full Request remains available to loaders, handlers, and metadata code.
|
|
168
|
+
*/
|
|
169
|
+
export function withTrackedRouteLocation<T extends object>(
|
|
170
|
+
values: T,
|
|
171
|
+
request: Request,
|
|
172
|
+
tracked: TrackedHeaderRequest | undefined,
|
|
173
|
+
location?: RouteLocation,
|
|
174
|
+
): T & { request: RouteLocation } {
|
|
175
|
+
const context = values as T & { request: RouteLocation };
|
|
176
|
+
let currentLocation = location ?? routeLocationFromRequest(request);
|
|
177
|
+
|
|
178
|
+
Object.defineProperty(context, "request", {
|
|
179
|
+
configurable: true,
|
|
180
|
+
enumerable: true,
|
|
181
|
+
get() {
|
|
182
|
+
tracked?.markRequestAccess();
|
|
183
|
+
return currentLocation;
|
|
184
|
+
},
|
|
185
|
+
set(value: RouteLocation) {
|
|
186
|
+
tracked?.markRequestAccess();
|
|
187
|
+
currentLocation = value;
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
return context;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/** Returns the URL fields that are safe to serialize into route HTML. */
|
|
195
|
+
export function routeLocationFromRequest(request: Request): RouteLocation {
|
|
196
|
+
return routeLocationFromUrl(new URL(request.url));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/** Returns a serializable route location from a URL without request metadata. */
|
|
200
|
+
export function routeLocationFromUrl(url: URL): RouteLocation {
|
|
201
|
+
return {
|
|
202
|
+
hash: url.hash,
|
|
203
|
+
pathname: url.pathname,
|
|
204
|
+
search: url.search,
|
|
205
|
+
url: url.href,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Returns the route location placeholder embedded in cached HTML. The client
|
|
211
|
+
* hydration runtime replaces it with document.URL before invoking shared code.
|
|
212
|
+
* Query values stay out of reusable HTML because they can contain credentials
|
|
213
|
+
* or other request-specific data.
|
|
214
|
+
*/
|
|
215
|
+
export function routeLocationForHydration(url: URL): RouteLocation {
|
|
216
|
+
return {
|
|
217
|
+
hash: "",
|
|
218
|
+
pathname: url.pathname,
|
|
219
|
+
search: "",
|
|
220
|
+
url: url.pathname,
|
|
221
|
+
};
|
|
222
|
+
}
|