@reckona/mreact-router 0.0.82 → 0.0.84
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/aws-lambda.d.ts.map +1 -1
- package/dist/adapters/aws-lambda.js +17 -22
- package/dist/adapters/aws-lambda.js.map +1 -1
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +3 -1
- package/dist/build.js.map +1 -1
- package/dist/client-route-inference.d.ts +2 -0
- package/dist/client-route-inference.d.ts.map +1 -0
- package/dist/client-route-inference.js +2 -0
- package/dist/client-route-inference.js.map +1 -0
- package/dist/client.d.ts +2 -1
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +25 -19
- package/dist/client.js.map +1 -1
- package/dist/layout-composer.d.ts +24 -0
- package/dist/layout-composer.d.ts.map +1 -0
- package/dist/layout-composer.js +83 -0
- package/dist/layout-composer.js.map +1 -0
- package/dist/metadata.d.ts +9 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +419 -0
- package/dist/metadata.js.map +1 -0
- package/dist/middleware.d.ts +22 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +94 -0
- package/dist/middleware.js.map +1 -0
- package/dist/module-runner.d.ts +2 -2
- package/dist/module-runner.d.ts.map +1 -1
- package/dist/module-runner.js +1 -1
- package/dist/module-runner.js.map +1 -1
- package/dist/navigation-runtime.d.ts +2 -0
- package/dist/navigation-runtime.d.ts.map +1 -0
- package/dist/navigation-runtime.js +2 -0
- package/dist/navigation-runtime.js.map +1 -0
- package/dist/preload-policy.d.ts +24 -0
- package/dist/preload-policy.d.ts.map +1 -0
- package/dist/preload-policy.js +45 -0
- package/dist/preload-policy.js.map +1 -0
- package/dist/render.d.ts +1 -3
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +32 -676
- package/dist/render.js.map +1 -1
- package/dist/route-hydration-contract.d.ts +11 -0
- package/dist/route-hydration-contract.d.ts.map +1 -0
- package/dist/route-hydration-contract.js +21 -0
- package/dist/route-hydration-contract.js.map +1 -0
- package/dist/route-module-loader.d.ts +10 -0
- package/dist/route-module-loader.d.ts.map +1 -0
- package/dist/route-module-loader.js +61 -0
- package/dist/route-module-loader.js.map +1 -0
- package/dist/route-source.d.ts.map +1 -1
- package/dist/route-source.js +7 -5
- package/dist/route-source.js.map +1 -1
- package/dist/routes.js +6 -0
- package/dist/routes.js.map +1 -1
- package/dist/serve.d.ts +13 -0
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +63 -28
- package/dist/serve.js.map +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +2 -1
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/adapters/aws-lambda.ts +21 -28
- package/src/build.ts +9 -6
- package/src/client-route-inference.ts +18 -0
- package/src/client.ts +32 -20
- package/src/layout-composer.ts +142 -0
- package/src/metadata.ts +578 -0
- package/src/middleware.ts +153 -0
- package/src/module-runner.ts +3 -2
- package/src/navigation-runtime.ts +16 -0
- package/src/preload-policy.ts +89 -0
- package/src/render.ts +75 -986
- package/src/route-hydration-contract.ts +22 -0
- package/src/route-module-loader.ts +95 -0
- package/src/route-source.ts +9 -4
- package/src/routes.ts +8 -0
- package/src/serve.ts +110 -29
- package/src/vite.ts +6 -4
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reckona/mreact-router",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.84",
|
|
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-
|
|
109
|
-
"@reckona/mreact
|
|
110
|
-
"@reckona/mreact-
|
|
111
|
-
"@reckona/mreact-
|
|
112
|
-
"@reckona/mreact-
|
|
113
|
-
"@reckona/mreact": "0.0.
|
|
114
|
-
"@reckona/mreact-reactive-dom": "0.0.
|
|
115
|
-
"@reckona/mreact-
|
|
116
|
-
"@reckona/mreact-
|
|
108
|
+
"@reckona/mreact-compat": "0.0.84",
|
|
109
|
+
"@reckona/mreact": "0.0.84",
|
|
110
|
+
"@reckona/mreact-devtools": "0.0.84",
|
|
111
|
+
"@reckona/mreact-query": "0.0.84",
|
|
112
|
+
"@reckona/mreact-reactive-core": "0.0.84",
|
|
113
|
+
"@reckona/mreact-compiler": "0.0.84",
|
|
114
|
+
"@reckona/mreact-reactive-dom": "0.0.84",
|
|
115
|
+
"@reckona/mreact-shared": "0.0.84",
|
|
116
|
+
"@reckona/mreact-server": "0.0.84"
|
|
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.84"
|
|
123
123
|
}
|
|
124
124
|
}
|
|
@@ -6,6 +6,10 @@ import { dirname, join } from "node:path";
|
|
|
6
6
|
import type { AppRouterServerActionOptions } from "../actions.js";
|
|
7
7
|
import type { AppRouterCache } from "../cache.js";
|
|
8
8
|
import type { AppRouterImportPolicy } from "../import-policy.js";
|
|
9
|
+
import {
|
|
10
|
+
normalizeBuiltAppRuntimePreloadStrategy,
|
|
11
|
+
type NormalizedBuiltAppRuntimePreloadStrategy,
|
|
12
|
+
} from "../preload-policy.js";
|
|
9
13
|
import {
|
|
10
14
|
emitRouterLog,
|
|
11
15
|
logDurationMs,
|
|
@@ -21,11 +25,10 @@ import type { RouterInstrumentation } from "../trace.js";
|
|
|
21
25
|
export type { RouterInstrumentation } from "../trace.js";
|
|
22
26
|
export type { AppRouterImportPolicy } from "../import-policy.js";
|
|
23
27
|
import {
|
|
28
|
+
createBuiltRequestRuntime,
|
|
24
29
|
preloadBuiltAppRuntime,
|
|
25
|
-
renderBuiltAppRequest,
|
|
26
30
|
resolveRequestHost,
|
|
27
31
|
warnIfImplicitHostTrust,
|
|
28
|
-
type BuiltAppRuntimePreloadStrategy,
|
|
29
32
|
type AppRouterPrerenderStore,
|
|
30
33
|
type RequestHostPolicy,
|
|
31
34
|
type ResponseSinkStrategy,
|
|
@@ -110,9 +113,7 @@ export type AwsLambdaPreloadStrategy =
|
|
|
110
113
|
wait?: "background" | "before-render" | "first-request" | undefined;
|
|
111
114
|
};
|
|
112
115
|
|
|
113
|
-
type NormalizedAwsLambdaPreloadStrategy =
|
|
114
|
-
wait: "background" | "before-render" | "first-request";
|
|
115
|
-
};
|
|
116
|
+
type NormalizedAwsLambdaPreloadStrategy = NormalizedBuiltAppRuntimePreloadStrategy;
|
|
116
117
|
|
|
117
118
|
type AwsLambdaDefaultPreloadMode = "all" | "middleware";
|
|
118
119
|
|
|
@@ -186,22 +187,24 @@ function createAwsLambdaRequestHandlerFromRuntime(
|
|
|
186
187
|
);
|
|
187
188
|
const importPolicy = await resolveAwsLambdaImportPolicy(options);
|
|
188
189
|
const renderStartedAt = phaseStartedAt(phases);
|
|
190
|
+
const builtRuntime = await createBuiltRequestRuntime({
|
|
191
|
+
immutableRuntime: true,
|
|
192
|
+
importPolicy,
|
|
193
|
+
outDir: options.outDir,
|
|
194
|
+
runtimeDir,
|
|
195
|
+
});
|
|
189
196
|
const preload = awsLambdaRenderPreload(
|
|
190
197
|
options,
|
|
191
198
|
runtimePreloadPromise,
|
|
192
199
|
defaultPreloadMode,
|
|
193
200
|
);
|
|
194
|
-
const response = await
|
|
195
|
-
outDir: options.outDir,
|
|
196
|
-
importPolicy,
|
|
201
|
+
const response = await builtRuntime.render(request, {
|
|
197
202
|
instrumentation: options.instrumentation,
|
|
198
203
|
logger: awsLambdaRenderLogger(options),
|
|
199
204
|
onResponse: options.onResponse,
|
|
200
205
|
...(preload === undefined ? {} : { preload }),
|
|
201
206
|
prerenderStore: options.prerenderStore,
|
|
202
|
-
request,
|
|
203
207
|
routeCache: options.routeCache,
|
|
204
|
-
runtimeDir,
|
|
205
208
|
serverActions: options.serverActions,
|
|
206
209
|
...(options.sinkStrategy === undefined ? {} : { sinkStrategy: options.sinkStrategy }),
|
|
207
210
|
});
|
|
@@ -416,19 +419,7 @@ function normalizeAwsLambdaPreload(
|
|
|
416
419
|
strategy: AwsLambdaPreloadStrategy | undefined,
|
|
417
420
|
defaultMode: AwsLambdaDefaultPreloadMode = "all",
|
|
418
421
|
): NormalizedAwsLambdaPreloadStrategy {
|
|
419
|
-
|
|
420
|
-
return { mode: defaultMode, wait: "background" };
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
if (typeof strategy === "string") {
|
|
424
|
-
return { mode: strategy, wait: "background" };
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
return {
|
|
428
|
-
mode: strategy.mode,
|
|
429
|
-
...(strategy.routes === undefined ? {} : { routes: strategy.routes }),
|
|
430
|
-
wait: strategy.wait ?? "background",
|
|
431
|
-
};
|
|
422
|
+
return normalizeBuiltAppRuntimePreloadStrategy(strategy, defaultMode);
|
|
432
423
|
}
|
|
433
424
|
|
|
434
425
|
function createAwsLambdaStreamingRequestHandlerFromRuntime<TContext = unknown>(
|
|
@@ -462,22 +453,24 @@ function createAwsLambdaStreamingRequestHandlerFromRuntime<TContext = unknown>(
|
|
|
462
453
|
);
|
|
463
454
|
const importPolicy = await resolveAwsLambdaImportPolicy(options);
|
|
464
455
|
const renderStartedAt = phaseStartedAt(phases);
|
|
456
|
+
const builtRuntime = await createBuiltRequestRuntime({
|
|
457
|
+
immutableRuntime: true,
|
|
458
|
+
importPolicy,
|
|
459
|
+
outDir: options.outDir,
|
|
460
|
+
runtimeDir,
|
|
461
|
+
});
|
|
465
462
|
const preload = awsLambdaRenderPreload(
|
|
466
463
|
options,
|
|
467
464
|
runtimePreloadPromise,
|
|
468
465
|
defaultPreloadMode,
|
|
469
466
|
);
|
|
470
|
-
const response = await
|
|
471
|
-
outDir: options.outDir,
|
|
472
|
-
importPolicy,
|
|
467
|
+
const response = await builtRuntime.render(request, {
|
|
473
468
|
instrumentation: options.instrumentation,
|
|
474
469
|
logger: awsLambdaRenderLogger(options),
|
|
475
470
|
onResponse: options.onResponse,
|
|
476
471
|
...(preload === undefined ? {} : { preload }),
|
|
477
472
|
prerenderStore: options.prerenderStore,
|
|
478
|
-
request,
|
|
479
473
|
routeCache: options.routeCache,
|
|
480
|
-
runtimeDir,
|
|
481
474
|
serverActions: options.serverActions,
|
|
482
475
|
...(options.sinkStrategy === undefined ? {} : { sinkStrategy: options.sinkStrategy }),
|
|
483
476
|
});
|
package/src/build.ts
CHANGED
|
@@ -13,9 +13,6 @@ import {
|
|
|
13
13
|
import { transformCompilerModuleContext } from "@reckona/mreact-compiler/internal";
|
|
14
14
|
import type { ServerOutputMode } from "@reckona/mreact-compiler";
|
|
15
15
|
import {
|
|
16
|
-
buildClientRouteBatchOutput,
|
|
17
|
-
buildNavigationRuntimeBundle,
|
|
18
|
-
clientScriptForPath,
|
|
19
16
|
compilerModuleContextForSource,
|
|
20
17
|
collectClientRouteReferences,
|
|
21
18
|
createClientRouteInferenceCache,
|
|
@@ -23,11 +20,16 @@ import {
|
|
|
23
20
|
detectNavigationRuntimeHint,
|
|
24
21
|
formatClientRouteInferenceDiagnostic,
|
|
25
22
|
inferClientRouteModule,
|
|
26
|
-
routeIdForPath,
|
|
27
|
-
type BuildClientRouteOutputOptions,
|
|
28
23
|
type ClientRouteManifestEntry,
|
|
29
24
|
type ClientRouteInferenceCache,
|
|
30
|
-
} from "./client.js";
|
|
25
|
+
} from "./client-route-inference.js";
|
|
26
|
+
import {
|
|
27
|
+
buildClientRouteBatchOutput,
|
|
28
|
+
buildNavigationRuntimeBundle,
|
|
29
|
+
clientScriptForPath,
|
|
30
|
+
routeIdForPath,
|
|
31
|
+
type BuildClientRouteOutputOptions,
|
|
32
|
+
} from "./navigation-runtime.js";
|
|
31
33
|
import { bundleAppRouterSourceModule, importAppRouterSourceModule } from "./module-runner.js";
|
|
32
34
|
import { scanAppRoutes } from "./routes.js";
|
|
33
35
|
import type { AppRoute } from "./routes.js";
|
|
@@ -3099,6 +3101,7 @@ const here = dirname(fileURLToPath(import.meta.url));
|
|
|
3099
3101
|
export const handler = await createPreloadedAwsLambdaRequestHandler({
|
|
3100
3102
|
importPolicy: "generated",
|
|
3101
3103
|
outDir: resolve(here, ${JSON.stringify(outDirRelativeToHandler)}),
|
|
3104
|
+
preload: { mode: "all" },
|
|
3102
3105
|
timings: process.env.MREACT_ROUTER_TIMINGS === "1",
|
|
3103
3106
|
});
|
|
3104
3107
|
`;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export {
|
|
2
|
+
collectClientRouteReferences,
|
|
3
|
+
compilerModuleContextForSource,
|
|
4
|
+
createClientRouteInferenceCache,
|
|
5
|
+
detectClientNavigationHint,
|
|
6
|
+
detectNavigationRuntimeHint,
|
|
7
|
+
formatClientRouteInferenceDiagnostic,
|
|
8
|
+
inferClientRouteModule,
|
|
9
|
+
isClientRouteModule,
|
|
10
|
+
isClientRouteSource,
|
|
11
|
+
routeToClientManifestEntry,
|
|
12
|
+
type ClientReferenceImport,
|
|
13
|
+
type ClientRouteInferenceCache,
|
|
14
|
+
type ClientRouteInferenceDiagnostic,
|
|
15
|
+
type ClientRouteInferenceResult,
|
|
16
|
+
type ClientRouteManifestEntry,
|
|
17
|
+
type ClientRouteReferenceResult,
|
|
18
|
+
} from "./client.js";
|
package/src/client.ts
CHANGED
|
@@ -10,10 +10,10 @@ import {
|
|
|
10
10
|
type BoundaryGraphResult,
|
|
11
11
|
type ClientRouteModuleAnalysis,
|
|
12
12
|
type ClientRouteStaticImportReference,
|
|
13
|
-
type ClientReferenceMetadata,
|
|
14
13
|
type StaticImportReference,
|
|
15
14
|
type TopLevelExportRenderInfo,
|
|
16
15
|
} from "@reckona/mreact-compiler";
|
|
16
|
+
import type { ClientReferenceMetadata } from "@reckona/mreact-shared/compiler-contract";
|
|
17
17
|
import {
|
|
18
18
|
collectClientRouteModuleAnalysisFromContext,
|
|
19
19
|
createCompilerModuleContext,
|
|
@@ -31,6 +31,11 @@ import {
|
|
|
31
31
|
} from "./bundle-pipeline.js";
|
|
32
32
|
import type { AppRoute } from "./routes.js";
|
|
33
33
|
import { existingRouteShellCandidates } from "./route-shells.js";
|
|
34
|
+
import {
|
|
35
|
+
routeDataScriptIds,
|
|
36
|
+
routeDataScriptSelector,
|
|
37
|
+
routeHydrationContract,
|
|
38
|
+
} from "./route-hydration-contract.js";
|
|
34
39
|
import { stripRouteClientSource } from "./route-source.js";
|
|
35
40
|
import { hasJsxSyntax } from "./source-jsx.js";
|
|
36
41
|
import { sourceModuleCandidates } from "./source-modules.js";
|
|
@@ -1375,7 +1380,7 @@ export function withHydrationMarkers(options: {
|
|
|
1375
1380
|
export function withRouteMarkers(options: { html: string; routePath: string }): string {
|
|
1376
1381
|
const routeId = routeIdForPath(options.routePath);
|
|
1377
1382
|
|
|
1378
|
-
return `<div
|
|
1383
|
+
return `<div ${routeHydrationContract.routeMarkerAttribute}="${escapeHtmlAttribute(routeId)}">${options.html}</div>`;
|
|
1379
1384
|
}
|
|
1380
1385
|
|
|
1381
1386
|
export function hydrationMarkerParts(options: {
|
|
@@ -1387,6 +1392,9 @@ export function hydrationMarkerParts(options: {
|
|
|
1387
1392
|
}): { prefix: string; suffix: string } {
|
|
1388
1393
|
const routeId = routeIdForPath(options.routePath);
|
|
1389
1394
|
const escapedRouteId = escapeHtmlAttribute(routeId);
|
|
1395
|
+
const [propsScriptId, clientReferencesScriptId] = routeDataScriptIds(routeId).map((id) =>
|
|
1396
|
+
escapeHtmlAttribute(id),
|
|
1397
|
+
);
|
|
1390
1398
|
const propsJson = escapeScriptJson(JSON.stringify(options.props));
|
|
1391
1399
|
const script = options.script ?? clientScriptForPath(options.routePath);
|
|
1392
1400
|
const scriptSrc = assetPath(script, options.assetBaseUrl ?? "/_mreact/client/");
|
|
@@ -1396,13 +1404,13 @@ export function hydrationMarkerParts(options: {
|
|
|
1396
1404
|
: escapeScriptJson(JSON.stringify(options.clientReferenceManifest));
|
|
1397
1405
|
|
|
1398
1406
|
return {
|
|
1399
|
-
prefix: `<div
|
|
1407
|
+
prefix: `<div ${routeHydrationContract.routeMarkerAttribute}="${escapedRouteId}">`,
|
|
1400
1408
|
suffix: [
|
|
1401
1409
|
"</div>",
|
|
1402
|
-
`<script type="application/json" id="
|
|
1410
|
+
`<script type="application/json" id="${propsScriptId}">${propsJson}</script>`,
|
|
1403
1411
|
clientReferencesJson === undefined
|
|
1404
1412
|
? undefined
|
|
1405
|
-
: `<script type="application/json" id="
|
|
1413
|
+
: `<script type="application/json" id="${clientReferencesScriptId}">${clientReferencesJson}</script>`,
|
|
1406
1414
|
`<script type="module" src="${escapeHtmlAttribute(scriptSrc)}"></script>`,
|
|
1407
1415
|
]
|
|
1408
1416
|
.filter((part): part is string => part !== undefined)
|
|
@@ -1758,7 +1766,7 @@ function __mreactResolveRouteNode(value) {
|
|
|
1758
1766
|
const boundaryOnlyHydrationBlock = routeRequiresFullHydration
|
|
1759
1767
|
? ""
|
|
1760
1768
|
: `${routeCellHydrationIndent}if (!__mreactHasNonSerializableClientBoundaries(__mreactMarker) && __mreactHydrateClientBoundaries(document, __mreactClientReferences, __mreactClientReferenceComponents)) {
|
|
1761
|
-
${routeCellHydrationIndent} __mreactMarker.setAttribute(
|
|
1769
|
+
${routeCellHydrationIndent} __mreactMarker.setAttribute(${JSON.stringify(routeHydrationContract.hydratedAttribute)}, "true");
|
|
1762
1770
|
${routeCellHydrationIndent} return;
|
|
1763
1771
|
${routeCellHydrationIndent}}
|
|
1764
1772
|
`;
|
|
@@ -1769,8 +1777,12 @@ ${routeCellHydrationIndent}}
|
|
|
1769
1777
|
const entry = `${routeCellEffectImport}${routeCleanupScopeImport}${emitCompatClientReferenceImportBlock(compatClientReferenceNames)}${clientReferenceImportBlock}${routeHydrationCode}
|
|
1770
1778
|
|
|
1771
1779
|
const __mreactRouteId = ${JSON.stringify(routeId)};
|
|
1772
|
-
const __mreactRouteStateSignature = ${JSON.stringify(routeStateSignature)};
|
|
1773
|
-
const
|
|
1780
|
+
const __mreactRouteStateSignature = ${JSON.stringify(routeStateSignature)};
|
|
1781
|
+
const __mreactRouteMarkerAttribute = ${JSON.stringify(routeHydrationContract.routeMarkerAttribute)};
|
|
1782
|
+
const __mreactRouteHydratedAttribute = ${JSON.stringify(routeHydrationContract.hydratedAttribute)};
|
|
1783
|
+
const __mreactPropsScriptPrefix = ${JSON.stringify(routeHydrationContract.propsScriptPrefix)};
|
|
1784
|
+
const __mreactClientReferencesScriptPrefix = ${JSON.stringify(routeHydrationContract.clientReferencesScriptPrefix)};
|
|
1785
|
+
const __mreactGlobal = globalThis;
|
|
1774
1786
|
${navigationStateDeclaration}
|
|
1775
1787
|
${routeCellStateDeclaration}
|
|
1776
1788
|
${routeCleanupStateDeclaration}
|
|
@@ -1780,9 +1792,9 @@ ${routeNodeResolver}
|
|
|
1780
1792
|
|
|
1781
1793
|
export function __mreactHydrateRoute() {
|
|
1782
1794
|
__mreactApplyOutOfOrderFragments(document);
|
|
1783
|
-
const __mreactMarker = document.querySelector(\`[
|
|
1784
|
-
const __mreactPropsElement = document.getElementById(
|
|
1785
|
-
const __mreactClientReferencesElement = document.getElementById(
|
|
1795
|
+
const __mreactMarker = document.querySelector(\`[\${__mreactRouteMarkerAttribute}="\${__mreactRouteId}"]\`);
|
|
1796
|
+
const __mreactPropsElement = document.getElementById(\`\${__mreactPropsScriptPrefix}\${__mreactRouteId}\`);
|
|
1797
|
+
const __mreactClientReferencesElement = document.getElementById(\`\${__mreactClientReferencesScriptPrefix}\${__mreactRouteId}\`);
|
|
1786
1798
|
const __mreactProps = __mreactPropsElement?.textContent === undefined
|
|
1787
1799
|
? {}
|
|
1788
1800
|
: JSON.parse(__mreactPropsElement.textContent);
|
|
@@ -1799,7 +1811,7 @@ export function __mreactHydrateRoute() {
|
|
|
1799
1811
|
${routeCellHydrationStart}${routeCleanupHydrationStart}${boundaryOnlyHydrationBlock}${routeComponentGuard}${routeCellHydrationIndent}const __mreactNode = ${routeNodeExpression};
|
|
1800
1812
|
${routeCellHydrationIndent}__mreactResumeRoute(__mreactMarker, __mreactNode);
|
|
1801
1813
|
${routeCellHydrationIndent}__mreactHydrateClientBoundaries(document, __mreactClientReferences, __mreactClientReferenceComponents);
|
|
1802
|
-
${routeCellHydrationIndent}__mreactMarker.setAttribute(
|
|
1814
|
+
${routeCellHydrationIndent}__mreactMarker.setAttribute(__mreactRouteHydratedAttribute, "true");
|
|
1803
1815
|
${routeCellHydrationEnd}}
|
|
1804
1816
|
${routeCellDropFunction}
|
|
1805
1817
|
${routeCleanupFunction}
|
|
@@ -2186,15 +2198,15 @@ function __mreactApplyNavigationHtml(html, url) {
|
|
|
2186
2198
|
const template = document.createElement("template");
|
|
2187
2199
|
template.innerHTML = html.replace(/^\\s*<!doctype html>/i, "");
|
|
2188
2200
|
__mreactApplyOutOfOrderFragments(template.content);
|
|
2189
|
-
const nextMarker = template.content.querySelector("[
|
|
2190
|
-
const currentMarker = document.querySelector("[
|
|
2201
|
+
const nextMarker = template.content.querySelector("[${routeHydrationContract.routeMarkerAttribute}]");
|
|
2202
|
+
const currentMarker = document.querySelector("[${routeHydrationContract.routeMarkerAttribute}]");
|
|
2191
2203
|
|
|
2192
2204
|
if (nextMarker === null || currentMarker === null) {
|
|
2193
2205
|
return false;
|
|
2194
2206
|
}
|
|
2195
2207
|
|
|
2196
|
-
const currentRouteId = currentMarker.getAttribute("
|
|
2197
|
-
const nextRouteId = nextMarker.getAttribute("
|
|
2208
|
+
const currentRouteId = currentMarker.getAttribute("${routeHydrationContract.routeMarkerAttribute}");
|
|
2209
|
+
const nextRouteId = nextMarker.getAttribute("${routeHydrationContract.routeMarkerAttribute}");
|
|
2198
2210
|
|
|
2199
2211
|
__mreactSyncHeadMetadata(template.content, html);
|
|
2200
2212
|
__mreactResumeNode(currentMarker, nextMarker);
|
|
@@ -2202,7 +2214,7 @@ ${routeCleanupNavigationDispose} __mreactSyncRouteDataScripts(template.content,
|
|
|
2202
2214
|
|
|
2203
2215
|
const script = template.content.querySelector('script[type="module"][src]')?.getAttribute("src");
|
|
2204
2216
|
if (script !== null && script !== undefined) {
|
|
2205
|
-
void import(/* @vite-ignore */ script).then((module) => module.
|
|
2217
|
+
void import(/* @vite-ignore */ script).then((module) => module.${routeHydrationContract.routeHydrateExport}?.());
|
|
2206
2218
|
}
|
|
2207
2219
|
|
|
2208
2220
|
__mreactApplyOutOfOrderFragments(document);
|
|
@@ -2295,15 +2307,15 @@ function __mreactRouteDataScriptIds(...routeIds) {
|
|
|
2295
2307
|
continue;
|
|
2296
2308
|
}
|
|
2297
2309
|
|
|
2298
|
-
ids.add(
|
|
2299
|
-
ids.add(
|
|
2310
|
+
ids.add(\`${routeHydrationContract.propsScriptPrefix}\${routeId}\`);
|
|
2311
|
+
ids.add(\`${routeHydrationContract.clientReferencesScriptPrefix}\${routeId}\`);
|
|
2300
2312
|
}
|
|
2301
2313
|
|
|
2302
2314
|
return ids;
|
|
2303
2315
|
}
|
|
2304
2316
|
|
|
2305
2317
|
function __mreactRouteDataScriptSelector() {
|
|
2306
|
-
return
|
|
2318
|
+
return ${JSON.stringify(routeDataScriptSelector())};
|
|
2307
2319
|
}
|
|
2308
2320
|
|
|
2309
2321
|
function __mreactCurrentHistoryState(url) {
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { relative, sep } from "node:path";
|
|
2
|
+
import { escapeHtmlAttribute } from "@reckona/mreact-shared/html-escape";
|
|
3
|
+
|
|
4
|
+
export interface SlotRenderContext {
|
|
5
|
+
consumedSlots: Set<string>;
|
|
6
|
+
namedSlots: Readonly<Record<string, string>>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface ShellFile {
|
|
10
|
+
file: string;
|
|
11
|
+
id: string;
|
|
12
|
+
kind: "layout" | "template";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface RenderedShellParts {
|
|
16
|
+
prefix: string;
|
|
17
|
+
suffix: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function createSlotRenderContext(
|
|
21
|
+
namedSlots: Readonly<Record<string, string>> = {},
|
|
22
|
+
): SlotRenderContext {
|
|
23
|
+
return {
|
|
24
|
+
consumedSlots: new Set(),
|
|
25
|
+
namedSlots,
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function splitLayoutSlot(
|
|
30
|
+
layoutHtml: string,
|
|
31
|
+
slotContext: SlotRenderContext = createSlotRenderContext(),
|
|
32
|
+
): RenderedShellParts {
|
|
33
|
+
const html = replaceNamedLayoutSlots(layoutHtml, slotContext);
|
|
34
|
+
const match = findDefaultLayoutSlot(html);
|
|
35
|
+
|
|
36
|
+
if (match === null) {
|
|
37
|
+
return { prefix: html, suffix: "" };
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
prefix: html.slice(0, match.index),
|
|
42
|
+
suffix: html.slice(match.index + match[0].length),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function markShellBoundary(html: string, shell: ShellFile): string {
|
|
47
|
+
const attributeName =
|
|
48
|
+
shell.kind === "layout" ? "data-mreact-layout-boundary" : "data-mreact-template-boundary";
|
|
49
|
+
|
|
50
|
+
if (html.includes(`${attributeName}=`)) {
|
|
51
|
+
return html;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return html.replace(
|
|
55
|
+
/<([A-Za-z][^\s/>]*)([^>]*)>/,
|
|
56
|
+
`<$1$2 ${attributeName}="${escapeHtmlAttribute(shell.id)}">`,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function shellBoundaryId(appDir: string, directory: string): string {
|
|
61
|
+
const relativeDirectory = relative(appDir, directory);
|
|
62
|
+
|
|
63
|
+
return relativeDirectory === ""
|
|
64
|
+
? "root"
|
|
65
|
+
: relativeDirectory.replaceAll(sep, "/").replace(/[^A-Za-z0-9_$/-]/g, "_");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function warnUnconsumedRouteSlots(options: {
|
|
69
|
+
appDir: string;
|
|
70
|
+
pageFile: string;
|
|
71
|
+
serverModuleCacheVersion: string | undefined;
|
|
72
|
+
slotContext: SlotRenderContext;
|
|
73
|
+
}): void {
|
|
74
|
+
if (options.serverModuleCacheVersion !== undefined) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const slotNames = Object.keys(options.slotContext.namedSlots);
|
|
79
|
+
if (slotNames.length === 0) {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const routeLabel = relative(options.appDir, options.pageFile).replaceAll(sep, "/");
|
|
84
|
+
|
|
85
|
+
for (const name of slotNames) {
|
|
86
|
+
if (name === "default") {
|
|
87
|
+
console.warn(
|
|
88
|
+
`[mreact] ${routeLabel}: slots.default does not target <Slot />; use the page body for default slot content.`,
|
|
89
|
+
);
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (!options.slotContext.consumedSlots.has(name)) {
|
|
94
|
+
console.warn(
|
|
95
|
+
`[mreact] ${routeLabel}: slots.{${name}} is not consumed by any ancestor layout or template.`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function replaceNamedLayoutSlots(layoutHtml: string, slotContext: SlotRenderContext): string {
|
|
102
|
+
return layoutHtml.replace(SLOT_TAG_PATTERN, (source, openAttributes: string) => {
|
|
103
|
+
const name = readSlotName(openAttributes);
|
|
104
|
+
|
|
105
|
+
if (name === undefined || name === "default") {
|
|
106
|
+
return source;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (Object.hasOwn(slotContext.namedSlots, name)) {
|
|
110
|
+
slotContext.consumedSlots.add(name);
|
|
111
|
+
return slotContext.namedSlots[name] ?? "";
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return "";
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const SLOT_TAG_PATTERN = /<slot\b([^>]*)>(?:<\/slot\s*>)?/g;
|
|
119
|
+
|
|
120
|
+
function findDefaultLayoutSlot(html: string): RegExpExecArray | null {
|
|
121
|
+
SLOT_TAG_PATTERN.lastIndex = 0;
|
|
122
|
+
|
|
123
|
+
for (;;) {
|
|
124
|
+
const match = SLOT_TAG_PATTERN.exec(html);
|
|
125
|
+
|
|
126
|
+
if (match === null) {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const name = readSlotName(match[1] ?? "");
|
|
131
|
+
|
|
132
|
+
if (name === undefined || name === "default") {
|
|
133
|
+
return match;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function readSlotName(attributes: string): string | undefined {
|
|
139
|
+
const match = /\bname\s*=\s*(?:"([^"]*)"|'([^']*)')/.exec(attributes);
|
|
140
|
+
|
|
141
|
+
return match?.[1] ?? match?.[2];
|
|
142
|
+
}
|