@reckona/mreact-router 0.0.174 → 0.0.176
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.map +1 -1
- package/dist/adapters/cloudflare.js +5 -49
- package/dist/adapters/cloudflare.js.map +1 -1
- package/dist/build.d.ts +1 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +90 -0
- package/dist/build.js.map +1 -1
- package/dist/built-assets.d.ts +11 -0
- package/dist/built-assets.d.ts.map +1 -0
- package/dist/built-assets.js +130 -0
- package/dist/built-assets.js.map +1 -0
- package/dist/built-runtime.d.ts +53 -0
- package/dist/built-runtime.d.ts.map +1 -0
- package/dist/built-runtime.js +178 -0
- package/dist/built-runtime.js.map +1 -0
- package/dist/built-server-module-artifacts.d.ts +19 -0
- package/dist/built-server-module-artifacts.d.ts.map +1 -0
- package/dist/built-server-module-artifacts.js +217 -0
- package/dist/built-server-module-artifacts.js.map +1 -0
- package/dist/client-manifest-assets.d.ts +16 -0
- package/dist/client-manifest-assets.d.ts.map +1 -0
- package/dist/client-manifest-assets.js +60 -0
- package/dist/client-manifest-assets.js.map +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +31 -3
- package/dist/client.js.map +1 -1
- package/dist/node-server.d.ts +29 -0
- package/dist/node-server.d.ts.map +1 -0
- package/dist/node-server.js +81 -0
- package/dist/node-server.js.map +1 -0
- package/dist/render.d.ts +9 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +20 -41
- package/dist/render.js.map +1 -1
- package/dist/route-dispatch.d.ts +2 -0
- package/dist/route-dispatch.d.ts.map +1 -0
- package/dist/route-dispatch.js +16 -0
- package/dist/route-dispatch.js.map +1 -0
- package/dist/route-loader-runtime.d.ts +22 -0
- package/dist/route-loader-runtime.d.ts.map +1 -0
- package/dist/route-loader-runtime.js +30 -0
- package/dist/route-loader-runtime.js.map +1 -0
- package/dist/routes.d.ts +2 -0
- package/dist/routes.d.ts.map +1 -1
- package/dist/routes.js +48 -1
- package/dist/routes.js.map +1 -1
- package/dist/serve.d.ts +4 -3
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +84 -582
- package/dist/serve.js.map +1 -1
- package/package.json +11 -11
- package/src/adapters/cloudflare.ts +5 -60
- package/src/build.ts +143 -0
- package/src/built-assets.ts +164 -0
- package/src/built-runtime.ts +312 -0
- package/src/built-server-module-artifacts.ts +340 -0
- package/src/client-manifest-assets.ts +92 -0
- package/src/client.ts +31 -3
- package/src/node-server.ts +129 -0
- package/src/render.ts +38 -60
- package/src/route-dispatch.ts +17 -0
- package/src/route-loader-runtime.ts +54 -0
- package/src/routes.ts +70 -1
- package/src/serve.ts +105 -863
package/src/serve.ts
CHANGED
|
@@ -1,19 +1,29 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import type {
|
|
6
|
-
BuiltPrerenderedRoute,
|
|
7
|
-
BuiltServerManifest,
|
|
8
|
-
BuiltServerModuleArtifact,
|
|
9
|
-
} from "./build.js";
|
|
1
|
+
import type { Server } from "node:http";
|
|
2
|
+
import { readFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import type { BuiltPrerenderedRoute } from "./build.js";
|
|
10
5
|
import type { AppRouterCache } from "./cache.js";
|
|
11
|
-
import type { ClientRouteManifestEntry } from "./client-route-inference.js";
|
|
12
6
|
import {
|
|
13
|
-
|
|
7
|
+
builtClientAssetPathname,
|
|
8
|
+
clearBuiltPublicAssetCacheForTest,
|
|
9
|
+
getBuiltPublicAssetCacheSizeForTest,
|
|
10
|
+
readBuiltClientAsset,
|
|
11
|
+
readBuiltPublicAsset,
|
|
12
|
+
} from "./built-assets.js";
|
|
13
|
+
import {
|
|
14
|
+
materializeBuiltRuntime,
|
|
15
|
+
mergeBuiltRuntimeImportPolicy,
|
|
16
|
+
readBuiltImportPolicyText,
|
|
17
|
+
type BuiltRuntime,
|
|
18
|
+
} from "./built-runtime.js";
|
|
19
|
+
import {
|
|
20
|
+
allBuiltServerModuleFiles,
|
|
21
|
+
loadBuiltServerModuleArtifacts,
|
|
22
|
+
loadBuiltServerModuleArtifactsForRequest,
|
|
23
|
+
} from "./built-server-module-artifacts.js";
|
|
24
|
+
import {
|
|
14
25
|
type AppRoute,
|
|
15
26
|
type MatchedRoute,
|
|
16
|
-
type RouteMatcher,
|
|
17
27
|
} from "./routes.js";
|
|
18
28
|
import type { AppRouterServerActionOptions } from "./actions.js";
|
|
19
29
|
import type { AppRouterImportPolicy } from "./import-policy.js";
|
|
@@ -21,75 +31,27 @@ import {
|
|
|
21
31
|
preloadBuiltRequestModules,
|
|
22
32
|
renderAppRequest,
|
|
23
33
|
resolveAppRouterMiddleware,
|
|
34
|
+
type AppRouterServerRenderArtifactLoader,
|
|
24
35
|
type AppRouterRenderPreload,
|
|
25
36
|
type AppRouterResponseHook,
|
|
26
|
-
type
|
|
37
|
+
type RenderAppRequestRuntimeOptions,
|
|
27
38
|
} from "./render.js";
|
|
28
39
|
import type { RouterInstrumentation } from "./trace.js";
|
|
29
40
|
import {
|
|
30
41
|
bytesResponse,
|
|
31
42
|
htmlResponse,
|
|
32
|
-
nodeRequestToWebRequest,
|
|
33
|
-
rawNodeRequestUrl,
|
|
34
|
-
sendResponse,
|
|
35
43
|
} from "./http.js";
|
|
36
44
|
import {
|
|
37
45
|
emitRouterLog,
|
|
38
46
|
logDurationMs,
|
|
39
|
-
logError,
|
|
40
47
|
logNow,
|
|
41
|
-
nodeRequestPath,
|
|
42
|
-
requestLogFields,
|
|
43
48
|
type AppRouterLogger,
|
|
44
49
|
} from "./logger.js";
|
|
50
|
+
import { startNodeRequestServer } from "./node-server.js";
|
|
45
51
|
import { builtAppRuntimePreloadPlan } from "./preload-policy.js";
|
|
46
|
-
import { routeShellCandidates } from "./route-shells.js";
|
|
47
52
|
import { normalizeRoutePath } from "./route-path.js";
|
|
48
53
|
import type { HttpUpgradeHandler } from "./upgrade.js";
|
|
49
54
|
|
|
50
|
-
interface BuiltRuntime {
|
|
51
|
-
appDir: string;
|
|
52
|
-
allowedSourceDirs: readonly string[];
|
|
53
|
-
assetBaseUrl?: string | undefined;
|
|
54
|
-
clientAssetPaths: ReadonlySet<string>;
|
|
55
|
-
clientScripts: ReadonlyMap<string, string>;
|
|
56
|
-
clientStylesByFile: ReadonlyMap<string, readonly string[]>;
|
|
57
|
-
clientStyles: ReadonlyMap<string, readonly string[]>;
|
|
58
|
-
generatedImportPolicy?: AppRouterImportPolicy | undefined;
|
|
59
|
-
hasMiddleware: boolean;
|
|
60
|
-
navigationScripts: ReadonlyMap<string, string>;
|
|
61
|
-
projectRoot: string;
|
|
62
|
-
publicAssetBaseUrl?: string | undefined;
|
|
63
|
-
prerenderableRoutes: ReadonlySet<string>;
|
|
64
|
-
prerenderLocks: Map<string, Promise<Response>>;
|
|
65
|
-
prerenderedRoutes: Map<string, BuiltPrerenderedRoute>;
|
|
66
|
-
routeMatcher: RouteMatcher;
|
|
67
|
-
routes: readonly AppRoute[];
|
|
68
|
-
serverActionReferencesByFile: ReadonlyMap<
|
|
69
|
-
string,
|
|
70
|
-
readonly {
|
|
71
|
-
end: number;
|
|
72
|
-
expression: string;
|
|
73
|
-
expressionEnd: number;
|
|
74
|
-
expressionStart: number;
|
|
75
|
-
moduleId: string;
|
|
76
|
-
exportName: string;
|
|
77
|
-
inferred: boolean;
|
|
78
|
-
sourceHash: string;
|
|
79
|
-
start: number;
|
|
80
|
-
}[]
|
|
81
|
-
>;
|
|
82
|
-
serverActionManifest?: readonly { moduleId: string; exportName: string; inferred?: boolean }[] | undefined;
|
|
83
|
-
serverModuleArtifactLoads: Map<string, Promise<void>>;
|
|
84
|
-
serverModuleClosureFiles: Map<string, readonly string[]>;
|
|
85
|
-
serverModuleFiles: ReadonlyMap<string, string>;
|
|
86
|
-
serverModuleRenderFiles: ReadonlyMap<string, string>;
|
|
87
|
-
serverModuleRequestFiles: ReadonlyMap<string, string>;
|
|
88
|
-
serverModules: Map<string, BuiltServerModuleArtifact>;
|
|
89
|
-
serverModuleCacheVersion: string;
|
|
90
|
-
serverSourceFiles: ReadonlyMap<string, string>;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
55
|
interface BuiltRuntimeCacheEntry {
|
|
94
56
|
clientManifestText: string;
|
|
95
57
|
importPolicyText: string | undefined;
|
|
@@ -98,12 +60,8 @@ interface BuiltRuntimeCacheEntry {
|
|
|
98
60
|
}
|
|
99
61
|
|
|
100
62
|
const builtRuntimeCache = new Map<string, BuiltRuntimeCacheEntry>();
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
interface BuiltPublicAsset {
|
|
104
|
-
bytes: Uint8Array;
|
|
105
|
-
headers: HeadersInit;
|
|
106
|
-
}
|
|
63
|
+
const builtRuntimeReadInflight = new Map<string, Promise<BuiltRuntime>>();
|
|
64
|
+
let builtRuntimeMaterializeCountForTest = 0;
|
|
107
65
|
|
|
108
66
|
/**
|
|
109
67
|
* Strategy for the final response body shape sent to the HTTP layer.
|
|
@@ -615,30 +573,18 @@ export async function startServer(
|
|
|
615
573
|
importPolicy: options.importPolicy,
|
|
616
574
|
outDir: options.outDir,
|
|
617
575
|
});
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
fallbackHost,
|
|
631
|
-
hostPolicy: options.hostPolicy,
|
|
632
|
-
rawHost: incoming.headers.host,
|
|
633
|
-
});
|
|
634
|
-
const origin = `http://${host}`;
|
|
635
|
-
const request = nodeRequestToWebRequest(incoming, origin);
|
|
636
|
-
const logFields = requestLogFields(request, "node");
|
|
637
|
-
emitRouterLog(options.logger, "info", {
|
|
638
|
-
...logFields,
|
|
639
|
-
type: "router:request:start",
|
|
640
|
-
});
|
|
641
|
-
const response = await runtime.render(request, {
|
|
576
|
+
|
|
577
|
+
return await startNodeRequestServer({
|
|
578
|
+
allowedHosts: options.allowedHosts,
|
|
579
|
+
errorHandler: options.errorHandler,
|
|
580
|
+
hostname: options.hostname,
|
|
581
|
+
hostPolicy: options.hostPolicy,
|
|
582
|
+
logger: options.logger,
|
|
583
|
+
onUpgrade: options.onUpgrade,
|
|
584
|
+
port: options.port,
|
|
585
|
+
resolveHost: resolveRequestHost,
|
|
586
|
+
render: (request) =>
|
|
587
|
+
runtime.render(request, {
|
|
642
588
|
instrumentation: options.instrumentation,
|
|
643
589
|
logger: options.logger,
|
|
644
590
|
onResponse: options.onResponse,
|
|
@@ -646,217 +592,28 @@ export async function startServer(
|
|
|
646
592
|
routeCache: options.routeCache,
|
|
647
593
|
serverActions: options.serverActions,
|
|
648
594
|
...(options.sinkStrategy === undefined ? {} : { sinkStrategy: options.sinkStrategy }),
|
|
649
|
-
})
|
|
650
|
-
emitRouterLog(options.logger, "info", {
|
|
651
|
-
...logFields,
|
|
652
|
-
durationMs: logDurationMs(startedAt),
|
|
653
|
-
status: response.status,
|
|
654
|
-
type: "router:request:end",
|
|
655
|
-
});
|
|
656
|
-
|
|
657
|
-
await sendResponse(outgoing, response);
|
|
658
|
-
} catch (error) {
|
|
659
|
-
// Log the full stack to stderr for operator visibility; never
|
|
660
|
-
// place it in the response body where attackers can scrape it
|
|
661
|
-
// (Issue 071). The errorHandler hook lets embedders customize
|
|
662
|
-
// the public response shape while still benefiting from the
|
|
663
|
-
// server-side log.
|
|
664
|
-
emitRouterLog(options.logger, "error", {
|
|
665
|
-
...fallbackRequestFields,
|
|
666
|
-
durationMs: logDurationMs(startedAt),
|
|
667
|
-
error: logError(error),
|
|
668
|
-
type: "router:request:error",
|
|
669
|
-
});
|
|
670
|
-
if (options.logger === undefined) {
|
|
671
|
-
console.error("[mreact] startServer request failed:", error);
|
|
672
|
-
}
|
|
673
|
-
const payload = options.errorHandler
|
|
674
|
-
? options.errorHandler(error)
|
|
675
|
-
: { body: "Internal Server Error", status: 500 };
|
|
676
|
-
outgoing.statusCode = payload.status;
|
|
677
|
-
outgoing.setHeader(
|
|
678
|
-
"content-type",
|
|
679
|
-
payload.headers?.["content-type"] ?? "text/plain; charset=utf-8",
|
|
680
|
-
);
|
|
681
|
-
for (const [name, value] of Object.entries(payload.headers ?? {})) {
|
|
682
|
-
if (name.toLowerCase() === "content-type") continue;
|
|
683
|
-
outgoing.setHeader(name, value);
|
|
684
|
-
}
|
|
685
|
-
outgoing.end(payload.body);
|
|
686
|
-
}
|
|
595
|
+
}),
|
|
687
596
|
});
|
|
688
|
-
|
|
689
|
-
if (options.onUpgrade !== undefined) {
|
|
690
|
-
server.on("upgrade", options.onUpgrade);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
await new Promise<void>((resolve) =>
|
|
694
|
-
server.listen(options.port, options.hostname ?? "127.0.0.1", resolve),
|
|
695
|
-
);
|
|
696
|
-
const address = server.address();
|
|
697
|
-
const port = typeof address === "object" && address !== null ? address.port : options.port;
|
|
698
|
-
|
|
699
|
-
return {
|
|
700
|
-
server,
|
|
701
|
-
url: `http://${options.hostname ?? "127.0.0.1"}:${port}`,
|
|
702
|
-
close: () =>
|
|
703
|
-
new Promise<void>((resolve, reject) =>
|
|
704
|
-
server.close((error) => (error ? reject(error) : resolve())),
|
|
705
|
-
),
|
|
706
|
-
};
|
|
707
|
-
}
|
|
708
|
-
|
|
709
|
-
async function readBuiltClientAsset(
|
|
710
|
-
outDir: string,
|
|
711
|
-
pathname: string,
|
|
712
|
-
allowedPaths: ReadonlySet<string>,
|
|
713
|
-
): Promise<Response> {
|
|
714
|
-
const clientPrefix = "/_mreact/client/";
|
|
715
|
-
const normalized = safeBuiltClientAssetPath(pathname.slice(clientPrefix.length));
|
|
716
|
-
|
|
717
|
-
if (normalized === undefined || !allowedPaths.has(normalized)) {
|
|
718
|
-
return new Response("Not Found", { status: 404 });
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
try {
|
|
722
|
-
const code = await readFile(join(outDir, "client", normalized), "utf8");
|
|
723
|
-
|
|
724
|
-
return new Response(code, {
|
|
725
|
-
headers: clientAssetHeaders(normalized),
|
|
726
|
-
});
|
|
727
|
-
} catch {
|
|
728
|
-
return new Response("Not Found", { status: 404 });
|
|
729
|
-
}
|
|
730
597
|
}
|
|
731
598
|
|
|
732
|
-
|
|
733
|
-
const rawUrl = rawNodeRequestUrl(request);
|
|
734
|
-
const rawPathname = rawUrl?.split(/[?#]/, 1)[0];
|
|
735
|
-
const pathname = rawPathname === undefined || rawPathname === "" ? url.pathname : rawPathname;
|
|
736
|
-
const normalizedPathname = pathname.startsWith("/") ? pathname : `/${pathname}`;
|
|
737
|
-
|
|
738
|
-
return normalizedPathname.startsWith("/_mreact/client/") ? normalizedPathname : undefined;
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
function safeBuiltClientAssetPath(relativePath: string): string | undefined {
|
|
742
|
-
let decoded: string;
|
|
743
|
-
|
|
744
|
-
try {
|
|
745
|
-
decoded = decodeURIComponent(relativePath);
|
|
746
|
-
} catch {
|
|
747
|
-
return undefined;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
if (decoded.includes("\\") || decoded.includes("\0")) {
|
|
751
|
-
return undefined;
|
|
752
|
-
}
|
|
753
|
-
|
|
754
|
-
if (decoded.split("/").some((segment) => segment === "..")) {
|
|
755
|
-
return undefined;
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
const normalized = normalize(decoded);
|
|
759
|
-
|
|
760
|
-
if (isAbsolute(normalized) || normalized === ".." || normalized.startsWith(`..${sep}`)) {
|
|
761
|
-
return undefined;
|
|
762
|
-
}
|
|
763
|
-
|
|
764
|
-
return normalized;
|
|
765
|
-
}
|
|
766
|
-
|
|
767
|
-
function builtClientAssetPaths(manifest: {
|
|
768
|
-
assets?: readonly string[] | undefined;
|
|
769
|
-
routes: readonly ClientRouteManifestEntry[];
|
|
770
|
-
}): ReadonlySet<string> {
|
|
771
|
-
const paths = new Set<string>(["manifest.json"]);
|
|
772
|
-
|
|
773
|
-
for (const route of manifest.routes) {
|
|
774
|
-
for (const asset of [
|
|
775
|
-
route.script,
|
|
776
|
-
route.sourceMap,
|
|
777
|
-
route.navigationScript,
|
|
778
|
-
...(route.css ?? []),
|
|
779
|
-
...(route.imports ?? []),
|
|
780
|
-
]) {
|
|
781
|
-
const path = safeClientManifestAssetPath(asset);
|
|
782
|
-
|
|
783
|
-
if (path !== undefined) {
|
|
784
|
-
paths.add(path);
|
|
785
|
-
}
|
|
786
|
-
}
|
|
787
|
-
}
|
|
788
|
-
|
|
789
|
-
for (const asset of manifest.assets ?? []) {
|
|
790
|
-
const path = safeClientManifestAssetPath(asset);
|
|
791
|
-
|
|
792
|
-
if (path !== undefined) {
|
|
793
|
-
paths.add(path);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
|
|
797
|
-
return paths;
|
|
798
|
-
}
|
|
799
|
-
|
|
800
|
-
function safeClientManifestAssetPath(asset: string | undefined): string | undefined {
|
|
801
|
-
if (asset === undefined || asset === "" || asset.startsWith("/") || asset.includes("\\")) {
|
|
802
|
-
return undefined;
|
|
803
|
-
}
|
|
804
|
-
|
|
805
|
-
const segments = asset.split("/");
|
|
599
|
+
export const __readBuiltPublicAssetForTest = readBuiltPublicAsset;
|
|
806
600
|
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
601
|
+
export function __clearBuiltRuntimeCacheForTest(): void {
|
|
602
|
+
builtRuntimeCache.clear();
|
|
603
|
+
builtRuntimeReadInflight.clear();
|
|
604
|
+
builtRuntimeMaterializeCountForTest = 0;
|
|
810
605
|
}
|
|
811
606
|
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
pathname: string,
|
|
815
|
-
): Promise<Response | undefined> {
|
|
816
|
-
const relativePath = pathname.startsWith("/") ? pathname.slice(1) : pathname;
|
|
817
|
-
|
|
818
|
-
if (relativePath === "") {
|
|
819
|
-
return undefined;
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
const normalized = normalize(relativePath);
|
|
823
|
-
|
|
824
|
-
if (isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../")) {
|
|
825
|
-
return undefined;
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
try {
|
|
829
|
-
const cacheKey = `${outDir}\0${normalized}`;
|
|
830
|
-
const cached = builtPublicAssetCache.get(cacheKey);
|
|
831
|
-
|
|
832
|
-
if (cached !== undefined) {
|
|
833
|
-
return bytesResponse(cached.bytes, {
|
|
834
|
-
headers: cached.headers,
|
|
835
|
-
});
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
const bytes = await readFile(join(outDir, "client", "public", normalized));
|
|
839
|
-
const headers = publicAssetHeaders(normalized);
|
|
840
|
-
|
|
841
|
-
builtPublicAssetCache.set(cacheKey, {
|
|
842
|
-
bytes,
|
|
843
|
-
headers,
|
|
844
|
-
});
|
|
845
|
-
|
|
846
|
-
return bytesResponse(bytes, { headers });
|
|
847
|
-
} catch {
|
|
848
|
-
return undefined;
|
|
849
|
-
}
|
|
607
|
+
export function __getBuiltRuntimeMaterializeCountForTest(): number {
|
|
608
|
+
return builtRuntimeMaterializeCountForTest;
|
|
850
609
|
}
|
|
851
610
|
|
|
852
|
-
export const __readBuiltPublicAssetForTest = readBuiltPublicAsset;
|
|
853
|
-
|
|
854
611
|
export function __clearBuiltPublicAssetCacheForTest(): void {
|
|
855
|
-
|
|
612
|
+
clearBuiltPublicAssetCacheForTest();
|
|
856
613
|
}
|
|
857
614
|
|
|
858
615
|
export function __getBuiltPublicAssetCacheSizeForTest(): number {
|
|
859
|
-
return
|
|
616
|
+
return getBuiltPublicAssetCacheSizeForTest();
|
|
860
617
|
}
|
|
861
618
|
|
|
862
619
|
async function readBuiltRuntime(options: {
|
|
@@ -873,6 +630,44 @@ async function readBuiltRuntime(options: {
|
|
|
873
630
|
return cached.runtime;
|
|
874
631
|
}
|
|
875
632
|
|
|
633
|
+
if (cached === undefined) {
|
|
634
|
+
const inflight = builtRuntimeReadInflight.get(cacheKey);
|
|
635
|
+
if (inflight !== undefined) {
|
|
636
|
+
return inflight;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
const runtime = readBuiltRuntimeUncached({
|
|
640
|
+
cacheKey,
|
|
641
|
+
cached,
|
|
642
|
+
outDir,
|
|
643
|
+
runtimeDir,
|
|
644
|
+
});
|
|
645
|
+
builtRuntimeReadInflight.set(cacheKey, runtime);
|
|
646
|
+
void runtime
|
|
647
|
+
.finally(() => {
|
|
648
|
+
if (builtRuntimeReadInflight.get(cacheKey) === runtime) {
|
|
649
|
+
builtRuntimeReadInflight.delete(cacheKey);
|
|
650
|
+
}
|
|
651
|
+
})
|
|
652
|
+
.catch(() => {});
|
|
653
|
+
return runtime;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
return readBuiltRuntimeUncached({
|
|
657
|
+
cacheKey,
|
|
658
|
+
cached,
|
|
659
|
+
outDir,
|
|
660
|
+
runtimeDir,
|
|
661
|
+
});
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
async function readBuiltRuntimeUncached(options: {
|
|
665
|
+
cached: BuiltRuntimeCacheEntry | undefined;
|
|
666
|
+
cacheKey: string;
|
|
667
|
+
outDir: string;
|
|
668
|
+
runtimeDir: string;
|
|
669
|
+
}): Promise<BuiltRuntime> {
|
|
670
|
+
const { cacheKey, cached, outDir, runtimeDir } = options;
|
|
876
671
|
const serverManifestPath = join(outDir, "server", "manifest.json");
|
|
877
672
|
const clientManifestPath = join(outDir, "client", "manifest.json");
|
|
878
673
|
const importPolicyPath = join(outDir, "server", "import-policy.json");
|
|
@@ -896,6 +691,9 @@ async function readBuiltRuntime(options: {
|
|
|
896
691
|
clientManifestPath,
|
|
897
692
|
importPolicyText,
|
|
898
693
|
importPolicyPath,
|
|
694
|
+
onMaterialize() {
|
|
695
|
+
builtRuntimeMaterializeCountForTest += 1;
|
|
696
|
+
},
|
|
899
697
|
outDir,
|
|
900
698
|
runtimeDir,
|
|
901
699
|
serverManifestText,
|
|
@@ -908,209 +706,13 @@ async function readBuiltRuntime(options: {
|
|
|
908
706
|
runtime,
|
|
909
707
|
serverManifestText,
|
|
910
708
|
});
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
async function materializeBuiltRuntime(options: {
|
|
916
|
-
clientManifestText: string;
|
|
917
|
-
clientManifestPath: string;
|
|
918
|
-
importPolicyPath: string;
|
|
919
|
-
importPolicyText: string | undefined;
|
|
920
|
-
outDir: string;
|
|
921
|
-
runtimeDir: string;
|
|
922
|
-
serverManifestText: string;
|
|
923
|
-
serverManifestPath: string;
|
|
924
|
-
}): Promise<BuiltRuntime> {
|
|
925
|
-
const serverManifest = parseBuiltJsonArtifact<BuiltServerManifest>(
|
|
926
|
-
options.serverManifestText,
|
|
927
|
-
options.serverManifestPath,
|
|
928
|
-
"built app server manifest",
|
|
929
|
-
);
|
|
930
|
-
const clientManifest = parseBuiltJsonArtifact<{
|
|
931
|
-
assets?: readonly string[];
|
|
932
|
-
routes: ClientRouteManifestEntry[];
|
|
933
|
-
styles?: Array<{ css?: readonly string[]; file: string }>;
|
|
934
|
-
}>(options.clientManifestText, options.clientManifestPath, "built app client manifest");
|
|
935
|
-
const appDir = await materializeBuiltServerApp(options.runtimeDir, serverManifest);
|
|
936
|
-
const projectRoot = appDir;
|
|
937
|
-
const routesDir = join(projectRoot, serverManifest.routesDir ?? "");
|
|
938
|
-
const routes = serverManifest.routes.map((route) => ({
|
|
939
|
-
...route,
|
|
940
|
-
file: join(projectRoot, route.file),
|
|
941
|
-
}));
|
|
942
|
-
const prerenderedRoutes = new Map(Object.entries(serverManifest.prerenderedRoutes ?? {}));
|
|
943
|
-
const prerenderableRoutes = new Set(prerenderedRoutes.keys());
|
|
944
|
-
const prerenderLocks = new Map<string, Promise<Response>>();
|
|
945
|
-
const serverModules = new Map<string, BuiltServerModuleArtifact>(
|
|
946
|
-
Object.entries(serverManifest.serverModules ?? {}).map(([file, artifact]) => [
|
|
947
|
-
join(appDir, file),
|
|
948
|
-
artifact,
|
|
949
|
-
]),
|
|
950
|
-
);
|
|
951
|
-
const serverModuleFiles = new Map(
|
|
952
|
-
Object.entries(serverManifest.serverModuleFiles ?? {}).map(([file, artifactFile]) => [
|
|
953
|
-
join(appDir, file),
|
|
954
|
-
join(options.outDir, "server", safeManifestFilePath(artifactFile)),
|
|
955
|
-
]),
|
|
956
|
-
);
|
|
957
|
-
const serverModuleRequestFiles = new Map(
|
|
958
|
-
Object.entries(serverManifest.serverModuleRequestFiles ?? {}).map(([file, artifactFile]) => [
|
|
959
|
-
join(appDir, file),
|
|
960
|
-
join(options.outDir, "server", safeManifestFilePath(artifactFile)),
|
|
961
|
-
]),
|
|
962
|
-
);
|
|
963
|
-
const serverModuleRenderFiles = new Map(
|
|
964
|
-
Object.entries(serverManifest.serverModuleRenderFiles ?? {}).map(([file, artifactFile]) => [
|
|
965
|
-
join(appDir, file),
|
|
966
|
-
join(options.outDir, "server", safeManifestFilePath(artifactFile)),
|
|
967
|
-
]),
|
|
968
|
-
);
|
|
969
|
-
const serverSourceFiles = new Map(
|
|
970
|
-
Object.entries(serverManifest.files).map(([file, source]) => [join(appDir, file), source]),
|
|
971
|
-
);
|
|
972
|
-
const serverActionReferencesByFile = new Map(
|
|
973
|
-
Object.entries(serverManifest.routeServerActionReferences ?? {}).map(([file, references]) => [
|
|
974
|
-
join(appDir, file),
|
|
975
|
-
references,
|
|
976
|
-
]),
|
|
977
|
-
);
|
|
978
|
-
const routeMatcher = createRouteMatcher(routes, serverManifest.routeMatcher);
|
|
979
|
-
const clientScripts = new Map(
|
|
980
|
-
clientManifest.routes.flatMap((route) =>
|
|
981
|
-
route.client && route.script !== undefined ? [[route.path, route.script]] : [],
|
|
982
|
-
),
|
|
983
|
-
);
|
|
984
|
-
const clientStyles = new Map(
|
|
985
|
-
clientManifest.routes.flatMap((route) =>
|
|
986
|
-
route.css !== undefined && route.css.length > 0 ? [[route.path, route.css]] : [],
|
|
987
|
-
),
|
|
988
|
-
);
|
|
989
|
-
const clientStylesByFile = new Map(
|
|
990
|
-
(clientManifest.styles ?? []).flatMap((style) =>
|
|
991
|
-
style.css !== undefined && style.css.length > 0
|
|
992
|
-
? [[join(routesDir, style.file), style.css] as const]
|
|
993
|
-
: [],
|
|
994
|
-
),
|
|
995
|
-
);
|
|
996
|
-
const navigationScripts = new Map(
|
|
997
|
-
clientManifest.routes.flatMap((route) =>
|
|
998
|
-
route.navigation === true && route.navigationScript !== undefined
|
|
999
|
-
? [[route.path, route.navigationScript]]
|
|
1000
|
-
: [],
|
|
1001
|
-
),
|
|
1002
|
-
);
|
|
1003
|
-
const hasMiddleware =
|
|
1004
|
-
serverSourceFiles.has(join(routesDir, "middleware.ts")) ||
|
|
1005
|
-
serverSourceFiles.has(join(routesDir, "middleware.mreact.ts"));
|
|
1006
|
-
const serverModuleCacheVersion = createHash("sha256")
|
|
1007
|
-
.update(options.serverManifestText)
|
|
1008
|
-
.update("\0")
|
|
1009
|
-
.update(options.clientManifestText)
|
|
1010
|
-
.digest("hex")
|
|
1011
|
-
.slice(0, 16);
|
|
1012
|
-
|
|
1013
|
-
const allowedSourceDirs = (serverManifest.allowedSourceDirs ?? [""]).map((directory) =>
|
|
1014
|
-
join(projectRoot, directory),
|
|
1015
|
-
);
|
|
1016
|
-
const generatedImportPolicy = builtGeneratedImportPolicy(
|
|
1017
|
-
options.importPolicyText,
|
|
1018
|
-
options.importPolicyPath,
|
|
1019
|
-
);
|
|
1020
|
-
|
|
1021
|
-
return {
|
|
1022
|
-
appDir: routesDir,
|
|
1023
|
-
allowedSourceDirs,
|
|
1024
|
-
...(serverManifest.assetBaseUrl === undefined
|
|
1025
|
-
? {}
|
|
1026
|
-
: { assetBaseUrl: serverManifest.assetBaseUrl }),
|
|
1027
|
-
clientAssetPaths: builtClientAssetPaths(clientManifest),
|
|
1028
|
-
clientScripts,
|
|
1029
|
-
clientStylesByFile,
|
|
1030
|
-
clientStyles,
|
|
1031
|
-
...(generatedImportPolicy === undefined ? {} : { generatedImportPolicy }),
|
|
1032
|
-
hasMiddleware,
|
|
1033
|
-
navigationScripts,
|
|
1034
|
-
projectRoot,
|
|
1035
|
-
...(serverManifest.publicAssetBaseUrl === undefined
|
|
1036
|
-
? {}
|
|
1037
|
-
: { publicAssetBaseUrl: serverManifest.publicAssetBaseUrl }),
|
|
1038
|
-
prerenderableRoutes,
|
|
1039
|
-
prerenderLocks,
|
|
1040
|
-
prerenderedRoutes,
|
|
1041
|
-
routeMatcher,
|
|
1042
|
-
routes,
|
|
1043
|
-
serverActionReferencesByFile,
|
|
1044
|
-
...(serverManifest.serverActionManifest === undefined
|
|
1045
|
-
? {}
|
|
1046
|
-
: { serverActionManifest: serverManifest.serverActionManifest }),
|
|
1047
|
-
serverModuleArtifactLoads: new Map(),
|
|
1048
|
-
serverModuleClosureFiles: new Map(),
|
|
1049
|
-
serverModuleFiles,
|
|
1050
|
-
serverModuleRenderFiles,
|
|
1051
|
-
serverModuleRequestFiles,
|
|
1052
|
-
serverModules,
|
|
1053
|
-
serverModuleCacheVersion,
|
|
1054
|
-
serverSourceFiles,
|
|
1055
|
-
};
|
|
1056
|
-
}
|
|
1057
|
-
|
|
1058
|
-
async function readBuiltImportPolicyText(outDir: string): Promise<string | undefined> {
|
|
1059
|
-
const policyPath = join(outDir, "server", "import-policy.json");
|
|
1060
|
-
|
|
1061
|
-
try {
|
|
1062
|
-
return await readFile(policyPath, "utf8");
|
|
1063
|
-
} catch (error) {
|
|
1064
|
-
if (isMissingFileError(error)) {
|
|
1065
|
-
return undefined;
|
|
709
|
+
runtime.catch(() => {
|
|
710
|
+
if (builtRuntimeCache.get(cacheKey)?.runtime === runtime) {
|
|
711
|
+
builtRuntimeCache.delete(cacheKey);
|
|
1066
712
|
}
|
|
713
|
+
});
|
|
1067
714
|
|
|
1068
|
-
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
|
|
1072
|
-
function builtGeneratedImportPolicy(
|
|
1073
|
-
importPolicyText: string | undefined,
|
|
1074
|
-
importPolicyPath: string,
|
|
1075
|
-
): AppRouterImportPolicy | undefined {
|
|
1076
|
-
if (importPolicyText === undefined) {
|
|
1077
|
-
return undefined;
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
const artifact = parseBuiltJsonArtifact<{
|
|
1081
|
-
runtimePackages?: unknown;
|
|
1082
|
-
}>(importPolicyText, importPolicyPath, "built app import policy");
|
|
1083
|
-
const runtimePackages = Array.isArray(artifact.runtimePackages)
|
|
1084
|
-
? artifact.runtimePackages.filter((name): name is string => typeof name === "string")
|
|
1085
|
-
: [];
|
|
1086
|
-
|
|
1087
|
-
return runtimePackages.length === 0 ? undefined : { allowedPackages: runtimePackages };
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
function mergeBuiltRuntimeImportPolicy(
|
|
1091
|
-
runtime: BuiltRuntime,
|
|
1092
|
-
importPolicy: AppRouterImportPolicy | undefined,
|
|
1093
|
-
): AppRouterImportPolicy | undefined {
|
|
1094
|
-
const generatedImportPolicy = runtime.generatedImportPolicy;
|
|
1095
|
-
|
|
1096
|
-
if (generatedImportPolicy === undefined) {
|
|
1097
|
-
return importPolicy;
|
|
1098
|
-
}
|
|
1099
|
-
|
|
1100
|
-
const allowedPackages = [
|
|
1101
|
-
...new Set([
|
|
1102
|
-
...(generatedImportPolicy.allowedPackages ?? []),
|
|
1103
|
-
...(importPolicy?.allowedPackages ?? []),
|
|
1104
|
-
]),
|
|
1105
|
-
];
|
|
1106
|
-
|
|
1107
|
-
return {
|
|
1108
|
-
...(allowedPackages.length === 0 ? {} : { allowedPackages }),
|
|
1109
|
-
...(importPolicy?.allowedSourceDirs === undefined
|
|
1110
|
-
? {}
|
|
1111
|
-
: { allowedSourceDirs: importPolicy.allowedSourceDirs }),
|
|
1112
|
-
...(importPolicy?.projectRoot === undefined ? {} : { projectRoot: importPolicy.projectRoot }),
|
|
1113
|
-
};
|
|
715
|
+
return runtime;
|
|
1114
716
|
}
|
|
1115
717
|
|
|
1116
718
|
function isMissingFileError(error: unknown): boolean {
|
|
@@ -1137,300 +739,6 @@ function builtArtifactReadError(label: string, artifactPath: string, error: unkn
|
|
|
1137
739
|
return new Error(`${prefix} ${label}: ${artifactPath}${detail}`, { cause: error });
|
|
1138
740
|
}
|
|
1139
741
|
|
|
1140
|
-
function parseBuiltJsonArtifact<T>(text: string, artifactPath: string, label: string): T {
|
|
1141
|
-
try {
|
|
1142
|
-
return JSON.parse(text) as T;
|
|
1143
|
-
} catch (error) {
|
|
1144
|
-
const detail = error instanceof Error && error.message !== "" ? `: ${error.message}` : "";
|
|
1145
|
-
|
|
1146
|
-
throw new Error(`Invalid ${label}: ${artifactPath}${detail}`, { cause: error });
|
|
1147
|
-
}
|
|
1148
|
-
}
|
|
1149
|
-
|
|
1150
|
-
async function loadBuiltServerModuleArtifacts(
|
|
1151
|
-
runtime: BuiltRuntime,
|
|
1152
|
-
files: Iterable<string>,
|
|
1153
|
-
kind: BuiltServerModuleArtifactKind = "all",
|
|
1154
|
-
): Promise<void> {
|
|
1155
|
-
for (const file of files) {
|
|
1156
|
-
await loadBuiltServerModuleArtifact(runtime, file, kind);
|
|
1157
|
-
}
|
|
1158
|
-
}
|
|
1159
|
-
|
|
1160
|
-
type BuiltServerModuleArtifactKind = "all" | "render" | "request";
|
|
1161
|
-
|
|
1162
|
-
async function loadBuiltServerModuleArtifact(
|
|
1163
|
-
runtime: BuiltRuntime,
|
|
1164
|
-
file: string,
|
|
1165
|
-
kind: BuiltServerModuleArtifactKind = "all",
|
|
1166
|
-
): Promise<void> {
|
|
1167
|
-
if (kind === "all") {
|
|
1168
|
-
await loadBuiltServerModuleArtifact(runtime, file, "request");
|
|
1169
|
-
await loadBuiltServerModuleArtifact(runtime, file, "render");
|
|
1170
|
-
return;
|
|
1171
|
-
}
|
|
1172
|
-
|
|
1173
|
-
const artifactPath =
|
|
1174
|
-
kind === "request"
|
|
1175
|
-
? runtime.serverModuleRequestFiles.get(file) ?? runtime.serverModuleFiles.get(file)
|
|
1176
|
-
: runtime.serverModuleRenderFiles.get(file) ?? runtime.serverModuleFiles.get(file);
|
|
1177
|
-
|
|
1178
|
-
if (artifactPath === undefined) {
|
|
1179
|
-
return;
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
const cached = runtime.serverModuleArtifactLoads.get(`${kind}\0${file}`);
|
|
1183
|
-
|
|
1184
|
-
if (cached !== undefined) {
|
|
1185
|
-
await cached;
|
|
1186
|
-
return;
|
|
1187
|
-
}
|
|
1188
|
-
|
|
1189
|
-
const loaded = readFile(artifactPath, "utf8")
|
|
1190
|
-
.then((text) => {
|
|
1191
|
-
const existing = runtime.serverModules.get(file) ?? {};
|
|
1192
|
-
runtime.serverModules.set(
|
|
1193
|
-
file,
|
|
1194
|
-
mergeBuiltServerModuleArtifacts(
|
|
1195
|
-
existing,
|
|
1196
|
-
hydrateBuiltServerModuleArtifact(
|
|
1197
|
-
parseBuiltJsonArtifact<BuiltServerModuleArtifact>(
|
|
1198
|
-
text,
|
|
1199
|
-
artifactPath,
|
|
1200
|
-
`built server module artifact for ${file}`,
|
|
1201
|
-
),
|
|
1202
|
-
builtServerDirForArtifactPath(artifactPath),
|
|
1203
|
-
),
|
|
1204
|
-
),
|
|
1205
|
-
);
|
|
1206
|
-
})
|
|
1207
|
-
.catch((error) => {
|
|
1208
|
-
runtime.serverModuleArtifactLoads.delete(`${kind}\0${file}`);
|
|
1209
|
-
if (isMissingFileError(error)) {
|
|
1210
|
-
throw builtArtifactReadError(`built server module artifact for ${file}`, artifactPath, error);
|
|
1211
|
-
}
|
|
1212
|
-
throw error;
|
|
1213
|
-
});
|
|
1214
|
-
runtime.serverModuleArtifactLoads.set(`${kind}\0${file}`, loaded);
|
|
1215
|
-
|
|
1216
|
-
await loaded;
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
function allBuiltServerModuleFiles(runtime: BuiltRuntime): Iterable<string> {
|
|
1220
|
-
return new Set([
|
|
1221
|
-
...runtime.serverModuleFiles.keys(),
|
|
1222
|
-
...runtime.serverModuleRequestFiles.keys(),
|
|
1223
|
-
...runtime.serverModuleRenderFiles.keys(),
|
|
1224
|
-
]);
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
function builtServerDirForArtifactPath(artifactPath: string): string {
|
|
1228
|
-
const marker = `${sep}server-modules${sep}`;
|
|
1229
|
-
const index = artifactPath.lastIndexOf(marker);
|
|
1230
|
-
|
|
1231
|
-
return index === -1 ? dirname(dirname(artifactPath)) : artifactPath.slice(0, index);
|
|
1232
|
-
}
|
|
1233
|
-
|
|
1234
|
-
function hydrateBuiltServerModuleArtifact(
|
|
1235
|
-
artifact: BuiltServerModuleArtifact,
|
|
1236
|
-
serverDir: string,
|
|
1237
|
-
): BuiltServerModuleArtifact {
|
|
1238
|
-
return {
|
|
1239
|
-
...(artifact.analysis === undefined ? {} : { analysis: artifact.analysis }),
|
|
1240
|
-
...(artifact.loader === undefined
|
|
1241
|
-
? {}
|
|
1242
|
-
: { loader: hydrateBuiltServerModuleOutput(artifact.loader, serverDir) }),
|
|
1243
|
-
...(artifact.routeMetadata === undefined
|
|
1244
|
-
? {}
|
|
1245
|
-
: { routeMetadata: hydrateBuiltServerModuleOutput(artifact.routeMetadata, serverDir) }),
|
|
1246
|
-
...(artifact.request === undefined
|
|
1247
|
-
? {}
|
|
1248
|
-
: { request: hydrateBuiltServerModuleOutput(artifact.request, serverDir) }),
|
|
1249
|
-
...(artifact.stream === undefined
|
|
1250
|
-
? {}
|
|
1251
|
-
: { stream: hydrateBuiltServerModuleOutput(artifact.stream, serverDir) }),
|
|
1252
|
-
...(artifact.string === undefined
|
|
1253
|
-
? {}
|
|
1254
|
-
: { string: hydrateBuiltServerModuleOutput(artifact.string, serverDir) }),
|
|
1255
|
-
};
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
function hydrateBuiltServerModuleOutput<T extends { moduleFile?: string | undefined }>(
|
|
1259
|
-
output: T,
|
|
1260
|
-
serverDir: string,
|
|
1261
|
-
): T {
|
|
1262
|
-
if (output.moduleFile === undefined || isAbsolute(output.moduleFile)) {
|
|
1263
|
-
return output;
|
|
1264
|
-
}
|
|
1265
|
-
|
|
1266
|
-
return {
|
|
1267
|
-
...output,
|
|
1268
|
-
moduleFile: join(serverDir, safeManifestFilePath(output.moduleFile)),
|
|
1269
|
-
};
|
|
1270
|
-
}
|
|
1271
|
-
|
|
1272
|
-
function mergeBuiltServerModuleArtifacts(
|
|
1273
|
-
existing: BuiltServerModuleArtifact,
|
|
1274
|
-
loaded: BuiltServerModuleArtifact,
|
|
1275
|
-
): BuiltServerModuleArtifact {
|
|
1276
|
-
return {
|
|
1277
|
-
...existing,
|
|
1278
|
-
...loaded,
|
|
1279
|
-
};
|
|
1280
|
-
}
|
|
1281
|
-
|
|
1282
|
-
async function loadBuiltServerModuleArtifactsForRequest(
|
|
1283
|
-
runtime: BuiltRuntime,
|
|
1284
|
-
routeFile: string | undefined,
|
|
1285
|
-
options: {
|
|
1286
|
-
includeRender?: boolean | undefined;
|
|
1287
|
-
includeShells?: boolean | undefined;
|
|
1288
|
-
} = {},
|
|
1289
|
-
): Promise<void> {
|
|
1290
|
-
const roots = [
|
|
1291
|
-
join(runtime.appDir, "middleware.ts"),
|
|
1292
|
-
join(runtime.appDir, "middleware.mreact.ts"),
|
|
1293
|
-
...(routeFile === undefined
|
|
1294
|
-
? []
|
|
1295
|
-
: [
|
|
1296
|
-
routeFile,
|
|
1297
|
-
...(options.includeShells === false ? [] : shellFilesForRoute(runtime, routeFile)),
|
|
1298
|
-
]),
|
|
1299
|
-
];
|
|
1300
|
-
const seen = new Set<string>();
|
|
1301
|
-
|
|
1302
|
-
for (const file of roots) {
|
|
1303
|
-
await loadBuiltServerModuleArtifactClosure(runtime, file, seen, "request");
|
|
1304
|
-
}
|
|
1305
|
-
|
|
1306
|
-
if (options.includeRender === true) {
|
|
1307
|
-
seen.clear();
|
|
1308
|
-
for (const file of roots) {
|
|
1309
|
-
await loadBuiltServerModuleArtifactClosure(runtime, file, seen, "render");
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
}
|
|
1313
|
-
|
|
1314
|
-
async function loadBuiltServerModuleArtifactClosure(
|
|
1315
|
-
runtime: BuiltRuntime,
|
|
1316
|
-
file: string,
|
|
1317
|
-
seen: Set<string>,
|
|
1318
|
-
kind: BuiltServerModuleArtifactKind,
|
|
1319
|
-
): Promise<void> {
|
|
1320
|
-
for (const closureFile of builtServerModuleClosureFiles(runtime, file)) {
|
|
1321
|
-
if (seen.has(closureFile)) {
|
|
1322
|
-
continue;
|
|
1323
|
-
}
|
|
1324
|
-
seen.add(closureFile);
|
|
1325
|
-
await loadBuiltServerModuleArtifact(runtime, closureFile, kind);
|
|
1326
|
-
}
|
|
1327
|
-
}
|
|
1328
|
-
|
|
1329
|
-
function builtServerModuleClosureFiles(
|
|
1330
|
-
runtime: BuiltRuntime,
|
|
1331
|
-
file: string,
|
|
1332
|
-
): readonly string[] {
|
|
1333
|
-
const cached = runtime.serverModuleClosureFiles.get(file);
|
|
1334
|
-
if (cached !== undefined) {
|
|
1335
|
-
return cached;
|
|
1336
|
-
}
|
|
1337
|
-
|
|
1338
|
-
const closure: string[] = [];
|
|
1339
|
-
collectBuiltServerModuleClosureFiles(runtime, file, new Set(), closure);
|
|
1340
|
-
runtime.serverModuleClosureFiles.set(file, closure);
|
|
1341
|
-
return closure;
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
function collectBuiltServerModuleClosureFiles(
|
|
1345
|
-
runtime: BuiltRuntime,
|
|
1346
|
-
file: string,
|
|
1347
|
-
seen: Set<string>,
|
|
1348
|
-
closure: string[],
|
|
1349
|
-
): void {
|
|
1350
|
-
if (seen.has(file)) {
|
|
1351
|
-
return;
|
|
1352
|
-
}
|
|
1353
|
-
seen.add(file);
|
|
1354
|
-
closure.push(file);
|
|
1355
|
-
|
|
1356
|
-
const source = runtime.serverSourceFiles.get(file);
|
|
1357
|
-
if (source === undefined) {
|
|
1358
|
-
return;
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
for (const specifier of localServerModuleSpecifiers(source)) {
|
|
1362
|
-
const resolved = resolveBuiltLocalServerSourceImport(runtime, file, specifier);
|
|
1363
|
-
if (resolved !== undefined) {
|
|
1364
|
-
collectBuiltServerModuleClosureFiles(runtime, resolved, seen, closure);
|
|
1365
|
-
}
|
|
1366
|
-
}
|
|
1367
|
-
}
|
|
1368
|
-
|
|
1369
|
-
const localServerModuleImportPattern =
|
|
1370
|
-
/\b(?:import|export)\s+(?:type\s+)?(?:[^"']*?\s+from\s*)?["'](?<source>\.{1,2}\/[^"']+)["']/g;
|
|
1371
|
-
|
|
1372
|
-
function localServerModuleSpecifiers(code: string): string[] {
|
|
1373
|
-
const specifiers = new Set<string>();
|
|
1374
|
-
localServerModuleImportPattern.lastIndex = 0;
|
|
1375
|
-
|
|
1376
|
-
for (const match of code.matchAll(localServerModuleImportPattern)) {
|
|
1377
|
-
const source = match.groups?.source;
|
|
1378
|
-
|
|
1379
|
-
if (source !== undefined) {
|
|
1380
|
-
specifiers.add(source);
|
|
1381
|
-
}
|
|
1382
|
-
}
|
|
1383
|
-
|
|
1384
|
-
return Array.from(specifiers);
|
|
1385
|
-
}
|
|
1386
|
-
|
|
1387
|
-
function resolveBuiltLocalServerSourceImport(
|
|
1388
|
-
runtime: BuiltRuntime,
|
|
1389
|
-
fromFile: string,
|
|
1390
|
-
specifier: string,
|
|
1391
|
-
): string | undefined {
|
|
1392
|
-
const base = join(dirname(fromFile), specifier);
|
|
1393
|
-
|
|
1394
|
-
for (const candidate of localServerSourceImportCandidates(base)) {
|
|
1395
|
-
if (runtime.serverSourceFiles.has(candidate)) {
|
|
1396
|
-
return candidate;
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
return undefined;
|
|
1401
|
-
}
|
|
1402
|
-
|
|
1403
|
-
function localServerSourceImportCandidates(base: string): string[] {
|
|
1404
|
-
const candidates = [base];
|
|
1405
|
-
|
|
1406
|
-
if (base.endsWith(".js")) {
|
|
1407
|
-
const withoutJs = base.slice(0, -".js".length);
|
|
1408
|
-
candidates.push(`${withoutJs}.ts`, `${withoutJs}.tsx`, `${withoutJs}.mreact.tsx`);
|
|
1409
|
-
} else if (base.endsWith(".jsx")) {
|
|
1410
|
-
const withoutJsx = base.slice(0, -".jsx".length);
|
|
1411
|
-
candidates.push(`${withoutJsx}.tsx`, `${withoutJsx}.mreact.tsx`);
|
|
1412
|
-
} else if (base.endsWith(".mreact")) {
|
|
1413
|
-
candidates.push(`${base}.tsx`);
|
|
1414
|
-
} else {
|
|
1415
|
-
candidates.push(
|
|
1416
|
-
`${base}.ts`,
|
|
1417
|
-
`${base}.tsx`,
|
|
1418
|
-
`${base}.mreact.tsx`,
|
|
1419
|
-
join(base, "index.ts"),
|
|
1420
|
-
join(base, "index.tsx"),
|
|
1421
|
-
join(base, "index.mreact.tsx"),
|
|
1422
|
-
);
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
|
-
return candidates;
|
|
1426
|
-
}
|
|
1427
|
-
|
|
1428
|
-
function shellFilesForRoute(runtime: BuiltRuntime, routeFile: string): string[] {
|
|
1429
|
-
return routeShellCandidates(runtime.appDir, routeFile)
|
|
1430
|
-
.map((candidate) => candidate.file)
|
|
1431
|
-
.filter((file) => runtime.serverSourceFiles.has(file));
|
|
1432
|
-
}
|
|
1433
|
-
|
|
1434
742
|
async function readPrerenderedRoute(
|
|
1435
743
|
runtime: BuiltRuntime,
|
|
1436
744
|
path: string,
|
|
@@ -1468,10 +776,15 @@ function builtRenderAppRequestOptions(
|
|
|
1468
776
|
requestUrl?: URL | undefined;
|
|
1469
777
|
runtime: BuiltRuntime;
|
|
1470
778
|
},
|
|
1471
|
-
):
|
|
1472
|
-
const
|
|
1473
|
-
|
|
1474
|
-
|
|
779
|
+
): RenderAppRequestRuntimeOptions {
|
|
780
|
+
const serverRenderArtifactLoader: AppRouterServerRenderArtifactLoader = {
|
|
781
|
+
async load(routeFile: string) {
|
|
782
|
+
await loadBuiltServerModuleArtifactsForRequest(options.runtime, routeFile, {
|
|
783
|
+
includeRender: true,
|
|
784
|
+
});
|
|
785
|
+
},
|
|
786
|
+
};
|
|
787
|
+
const renderOptions: RenderAppRequestRuntimeOptions = {
|
|
1475
788
|
appDir: options.runtime.appDir,
|
|
1476
789
|
assetBaseUrl: options.runtime.assetBaseUrl,
|
|
1477
790
|
clientScripts: options.runtime.clientScripts,
|
|
@@ -1491,11 +804,7 @@ function builtRenderAppRequestOptions(
|
|
|
1491
804
|
routeMatcher: options.runtime.routeMatcher,
|
|
1492
805
|
requestUrl: options.requestUrl,
|
|
1493
806
|
routes: options.runtime.routes,
|
|
1494
|
-
|
|
1495
|
-
await loadBuiltServerModuleArtifactsForRequest(options.runtime, routeFile, {
|
|
1496
|
-
includeRender: true,
|
|
1497
|
-
});
|
|
1498
|
-
},
|
|
807
|
+
serverRenderArtifactLoader,
|
|
1499
808
|
serverModules: options.runtime.serverModules,
|
|
1500
809
|
serverModuleCacheVersion: options.runtime.serverModuleCacheVersion,
|
|
1501
810
|
serverSourceFiles: options.runtime.serverSourceFiles,
|
|
@@ -1626,70 +935,3 @@ async function cloneResponse(response: Response): Promise<Response> {
|
|
|
1626
935
|
status: response.status,
|
|
1627
936
|
});
|
|
1628
937
|
}
|
|
1629
|
-
|
|
1630
|
-
async function materializeBuiltServerApp(
|
|
1631
|
-
runtimeDir: string,
|
|
1632
|
-
manifest: BuiltServerManifest,
|
|
1633
|
-
): Promise<string> {
|
|
1634
|
-
const appDir = join(runtimeDir, "app");
|
|
1635
|
-
|
|
1636
|
-
await rm(appDir, { force: true, recursive: true });
|
|
1637
|
-
await Promise.all(
|
|
1638
|
-
Object.entries(manifest.files).map(async ([file, code]) => {
|
|
1639
|
-
const outputFile = join(appDir, safeManifestFilePath(file));
|
|
1640
|
-
|
|
1641
|
-
await mkdir(dirname(outputFile), { recursive: true });
|
|
1642
|
-
await writeFile(outputFile, code);
|
|
1643
|
-
}),
|
|
1644
|
-
);
|
|
1645
|
-
|
|
1646
|
-
return appDir;
|
|
1647
|
-
}
|
|
1648
|
-
|
|
1649
|
-
function safeManifestFilePath(pathname: string): string {
|
|
1650
|
-
const normalized = normalize(pathname);
|
|
1651
|
-
|
|
1652
|
-
if (isAbsolute(normalized) || normalized === ".." || normalized.startsWith("../")) {
|
|
1653
|
-
throw new Error(`Invalid built app manifest file path: ${pathname}`);
|
|
1654
|
-
}
|
|
1655
|
-
|
|
1656
|
-
return normalized;
|
|
1657
|
-
}
|
|
1658
|
-
|
|
1659
|
-
function clientAssetHeaders(pathname: string): HeadersInit {
|
|
1660
|
-
if (pathname === "manifest.json") {
|
|
1661
|
-
return {
|
|
1662
|
-
"cache-control": "no-cache",
|
|
1663
|
-
"content-type": "application/json; charset=utf-8",
|
|
1664
|
-
};
|
|
1665
|
-
}
|
|
1666
|
-
|
|
1667
|
-
return {
|
|
1668
|
-
"cache-control": "public, max-age=31536000, immutable",
|
|
1669
|
-
"content-type": pathname.endsWith(".css")
|
|
1670
|
-
? "text/css; charset=utf-8"
|
|
1671
|
-
: "text/javascript; charset=utf-8",
|
|
1672
|
-
};
|
|
1673
|
-
}
|
|
1674
|
-
|
|
1675
|
-
function publicAssetHeaders(pathname: string): HeadersInit {
|
|
1676
|
-
return {
|
|
1677
|
-
"cache-control": "public, max-age=3600",
|
|
1678
|
-
"content-type": publicAssetContentType(pathname),
|
|
1679
|
-
};
|
|
1680
|
-
}
|
|
1681
|
-
|
|
1682
|
-
function publicAssetContentType(pathname: string): string {
|
|
1683
|
-
if (pathname.endsWith(".css")) return "text/css; charset=utf-8";
|
|
1684
|
-
if (pathname.endsWith(".html")) return "text/html; charset=utf-8";
|
|
1685
|
-
if (pathname.endsWith(".js")) return "text/javascript; charset=utf-8";
|
|
1686
|
-
if (pathname.endsWith(".json")) return "application/json; charset=utf-8";
|
|
1687
|
-
if (pathname.endsWith(".svg")) return "image/svg+xml";
|
|
1688
|
-
if (pathname.endsWith(".png")) return "image/png";
|
|
1689
|
-
if (pathname.endsWith(".jpg") || pathname.endsWith(".jpeg")) return "image/jpeg";
|
|
1690
|
-
if (pathname.endsWith(".webp")) return "image/webp";
|
|
1691
|
-
if (pathname.endsWith(".ico")) return "image/x-icon";
|
|
1692
|
-
if (pathname.endsWith(".txt")) return "text/plain; charset=utf-8";
|
|
1693
|
-
|
|
1694
|
-
return "application/octet-stream";
|
|
1695
|
-
}
|