@reckona/mreact-router 0.0.126 → 0.0.128
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/adapters/cloudflare.d.ts +3 -0
- package/dist/adapters/cloudflare.d.ts.map +1 -1
- package/dist/adapters/cloudflare.js +49 -8
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +3 -1
- package/dist/client.js.map +1 -1
- package/package.json +11 -11
- package/src/adapters/cloudflare.ts +80 -11
- package/src/client.ts +9 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reckona/mreact-router",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.128",
|
|
4
4
|
"description": "File-system app router, SSR, actions, and deployment adapters for mreact.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jsx",
|
|
@@ -105,20 +105,20 @@
|
|
|
105
105
|
},
|
|
106
106
|
"dependencies": {
|
|
107
107
|
"typescript": "^6.0.3",
|
|
108
|
-
"@reckona/mreact": "0.0.
|
|
109
|
-
"@reckona/mreact-compat": "0.0.
|
|
110
|
-
"@reckona/mreact-
|
|
111
|
-
"@reckona/mreact-query": "0.0.
|
|
112
|
-
"@reckona/mreact-
|
|
113
|
-
"@reckona/mreact-devtools": "0.0.
|
|
114
|
-
"@reckona/mreact-reactive-dom": "0.0.
|
|
115
|
-
"@reckona/mreact-
|
|
116
|
-
"@reckona/mreact-
|
|
108
|
+
"@reckona/mreact": "0.0.128",
|
|
109
|
+
"@reckona/mreact-compat": "0.0.128",
|
|
110
|
+
"@reckona/mreact-compiler": "0.0.128",
|
|
111
|
+
"@reckona/mreact-query": "0.0.128",
|
|
112
|
+
"@reckona/mreact-reactive-core": "0.0.128",
|
|
113
|
+
"@reckona/mreact-devtools": "0.0.128",
|
|
114
|
+
"@reckona/mreact-reactive-dom": "0.0.128",
|
|
115
|
+
"@reckona/mreact-server": "0.0.128",
|
|
116
|
+
"@reckona/mreact-shared": "0.0.128"
|
|
117
117
|
},
|
|
118
118
|
"peerDependencies": {
|
|
119
119
|
"vite": ">=8 <9"
|
|
120
120
|
},
|
|
121
121
|
"optionalDependencies": {
|
|
122
|
-
"@reckona/mreact-router-native": "0.0.
|
|
122
|
+
"@reckona/mreact-router-native": "0.0.128"
|
|
123
123
|
}
|
|
124
124
|
}
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import type { BuiltPrerenderedRoute, BuiltServerManifest } from "../build.js";
|
|
2
2
|
import type { ClientRouteManifestEntry } from "../client.js";
|
|
3
3
|
import type { AppRouterResponseHook } from "../render.js";
|
|
4
|
+
import {
|
|
5
|
+
__MREACT_QUERY_STATE_SCRIPT_ID,
|
|
6
|
+
createQueryClient,
|
|
7
|
+
dehydrate,
|
|
8
|
+
runWithQueryClient,
|
|
9
|
+
type DehydratedQueryClient,
|
|
10
|
+
type QueryClient,
|
|
11
|
+
} from "@reckona/mreact-query";
|
|
4
12
|
import type {
|
|
5
13
|
GenerateMetadataContext,
|
|
6
14
|
ManifestDescriptor,
|
|
@@ -80,6 +88,7 @@ export interface CloudflareBuiltRouteRenderContext<
|
|
|
80
88
|
export interface CloudflareRouteModuleLoaderContext<
|
|
81
89
|
Env = unknown,
|
|
82
90
|
> extends CloudflareBuiltRouteRenderContext<Env> {
|
|
91
|
+
queryClient: QueryClient;
|
|
83
92
|
request: Request;
|
|
84
93
|
}
|
|
85
94
|
|
|
@@ -94,6 +103,7 @@ export interface CloudflareRouteModuleComponentProps<
|
|
|
94
103
|
Env = unknown,
|
|
95
104
|
> extends CloudflareBuiltRouteRenderContext<Env> {
|
|
96
105
|
data: Data;
|
|
106
|
+
queryClient: QueryClient;
|
|
97
107
|
request: Request;
|
|
98
108
|
}
|
|
99
109
|
|
|
@@ -328,6 +338,7 @@ export function createCloudflareRouteModuleRenderer<Env = unknown>(
|
|
|
328
338
|
|
|
329
339
|
const pageModule = module as CloudflareRouteModule<unknown, Env>;
|
|
330
340
|
const component = selectCloudflarePageComponent(pageModule);
|
|
341
|
+
const queryClient = createQueryClient();
|
|
331
342
|
|
|
332
343
|
if (component === undefined) {
|
|
333
344
|
return new Response(
|
|
@@ -341,12 +352,16 @@ export function createCloudflareRouteModuleRenderer<Env = unknown>(
|
|
|
341
352
|
|
|
342
353
|
const loaderContext = {
|
|
343
354
|
...context,
|
|
355
|
+
queryClient,
|
|
344
356
|
request,
|
|
345
357
|
};
|
|
346
358
|
let data: unknown;
|
|
347
359
|
|
|
348
360
|
try {
|
|
349
|
-
data =
|
|
361
|
+
data =
|
|
362
|
+
pageModule.loader === undefined
|
|
363
|
+
? undefined
|
|
364
|
+
: await runWithQueryClient(queryClient, () => pageModule.loader!(loaderContext));
|
|
350
365
|
} catch (error) {
|
|
351
366
|
if (error instanceof Response) {
|
|
352
367
|
return error;
|
|
@@ -361,12 +376,13 @@ export function createCloudflareRouteModuleRenderer<Env = unknown>(
|
|
|
361
376
|
const props = {
|
|
362
377
|
...context,
|
|
363
378
|
data,
|
|
379
|
+
queryClient,
|
|
364
380
|
request,
|
|
365
381
|
};
|
|
366
382
|
let rendered: Awaited<ReturnType<typeof component>>;
|
|
367
383
|
|
|
368
384
|
try {
|
|
369
|
-
rendered = await component(props);
|
|
385
|
+
rendered = await runWithQueryClient(queryClient, () => component(props));
|
|
370
386
|
} catch (error) {
|
|
371
387
|
if (isNotFoundError(error)) {
|
|
372
388
|
return cloudflareNotFoundResponse(request);
|
|
@@ -380,7 +396,9 @@ export function createCloudflareRouteModuleRenderer<Env = unknown>(
|
|
|
380
396
|
}
|
|
381
397
|
|
|
382
398
|
const modulePreload = cloudflareModulePreloadTag(context.clientManifest, context.route.path);
|
|
383
|
-
const metadata = await
|
|
399
|
+
const metadata = await runWithQueryClient(queryClient, () =>
|
|
400
|
+
resolveCloudflareRouteMetadata([pageModule], props),
|
|
401
|
+
);
|
|
384
402
|
const body = withCloudflareHydrationMarkers({
|
|
385
403
|
data,
|
|
386
404
|
html: rendered,
|
|
@@ -392,16 +410,22 @@ export function createCloudflareRouteModuleRenderer<Env = unknown>(
|
|
|
392
410
|
const documented =
|
|
393
411
|
options.document === undefined
|
|
394
412
|
? defaultCloudflareDocument(body, modulePreload, metadata)
|
|
395
|
-
: await
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
413
|
+
: await runWithQueryClient(queryClient, () =>
|
|
414
|
+
options.document!({
|
|
415
|
+
...props,
|
|
416
|
+
body,
|
|
417
|
+
modulePreload,
|
|
418
|
+
}),
|
|
419
|
+
);
|
|
420
|
+
const documentedWithQueryState = await injectCloudflareQueryState(
|
|
421
|
+
documented,
|
|
422
|
+
dehydrate(queryClient),
|
|
423
|
+
);
|
|
400
424
|
|
|
401
425
|
return withDefaultSecurityHeaders(
|
|
402
|
-
|
|
403
|
-
?
|
|
404
|
-
: new Response(
|
|
426
|
+
documentedWithQueryState instanceof Response
|
|
427
|
+
? documentedWithQueryState
|
|
428
|
+
: new Response(documentedWithQueryState, {
|
|
405
429
|
headers: { "content-type": "text/html; charset=utf-8" },
|
|
406
430
|
}),
|
|
407
431
|
request,
|
|
@@ -409,6 +433,42 @@ export function createCloudflareRouteModuleRenderer<Env = unknown>(
|
|
|
409
433
|
};
|
|
410
434
|
}
|
|
411
435
|
|
|
436
|
+
async function injectCloudflareQueryState<T extends Response | string>(
|
|
437
|
+
document: T,
|
|
438
|
+
state: DehydratedQueryClient,
|
|
439
|
+
): Promise<T | Response | string> {
|
|
440
|
+
if (state.queries.length === 0) {
|
|
441
|
+
return document;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (typeof document === "string") {
|
|
445
|
+
return injectCloudflareQueryStateScript(document, state);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const contentType = document.headers.get("content-type") ?? "";
|
|
449
|
+
if (!/^text\/html\b/i.test(contentType)) {
|
|
450
|
+
return document;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const headers = new Headers(document.headers);
|
|
454
|
+
headers.delete("content-length");
|
|
455
|
+
return new Response(injectCloudflareQueryStateScript(await document.text(), state), {
|
|
456
|
+
headers,
|
|
457
|
+
status: document.status,
|
|
458
|
+
statusText: document.statusText,
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function injectCloudflareQueryStateScript(html: string, state: DehydratedQueryClient): string {
|
|
463
|
+
const script = `<script type="application/json" id="${__MREACT_QUERY_STATE_SCRIPT_ID}">${escapeJsonForHtml(
|
|
464
|
+
JSON.stringify(state),
|
|
465
|
+
)}</script>`;
|
|
466
|
+
|
|
467
|
+
return /<\/body>/i.test(html)
|
|
468
|
+
? html.replace(/<\/body>/i, () => `${script}</body>`)
|
|
469
|
+
: `${html}${script}`;
|
|
470
|
+
}
|
|
471
|
+
|
|
412
472
|
async function dispatchCloudflareServerRoute<Env>(
|
|
413
473
|
module: CloudflareServerRouteModule<Env>,
|
|
414
474
|
request: Request,
|
|
@@ -1225,6 +1285,15 @@ function escapeScriptJson(json: string): string {
|
|
|
1225
1285
|
return json.replaceAll("<", "\\u003c");
|
|
1226
1286
|
}
|
|
1227
1287
|
|
|
1288
|
+
function escapeJsonForHtml(value: string): string {
|
|
1289
|
+
return value
|
|
1290
|
+
.replaceAll("&", "\\u0026")
|
|
1291
|
+
.replaceAll("<", "\\u003c")
|
|
1292
|
+
.replaceAll(">", "\\u003e")
|
|
1293
|
+
.replaceAll("\u2028", "\\u2028")
|
|
1294
|
+
.replaceAll("\u2029", "\\u2029");
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1228
1297
|
function cloudflareRouteIdForPath(path: string): string {
|
|
1229
1298
|
if (path === "/") {
|
|
1230
1299
|
return "index";
|
package/src/client.ts
CHANGED
|
@@ -1110,7 +1110,15 @@ function isStyleModuleSpecifier(source: string): boolean {
|
|
|
1110
1110
|
const styleModuleExtensions = new Set([".css", ".less", ".sass", ".scss", ".styl", ".stylus"]);
|
|
1111
1111
|
|
|
1112
1112
|
function isClientBoundaryFallbackEligibleSource(source: string): boolean {
|
|
1113
|
-
|
|
1113
|
+
const sourceWithoutGuardedUndefinedCallbacks = source.replaceAll(
|
|
1114
|
+
/\bon[A-Z][A-Za-z0-9_$]*\s*=\s*\{\s*props\.[A-Za-z_$][\w$]*\s*===\s*undefined\s*\?\s*undefined\s*:/gu,
|
|
1115
|
+
"",
|
|
1116
|
+
);
|
|
1117
|
+
|
|
1118
|
+
return (
|
|
1119
|
+
!/\bon[A-Z][A-Za-z0-9_$]*\s*=/u.test(sourceWithoutGuardedUndefinedCallbacks) &&
|
|
1120
|
+
!/\bglobalThis\b/u.test(source)
|
|
1121
|
+
);
|
|
1114
1122
|
}
|
|
1115
1123
|
|
|
1116
1124
|
function renderedImportedExportNames(
|