@farm.js/core 0.1.0-beta.10 → 0.1.0-beta.13
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/{chunk-3QDB3IA6.mjs → chunk-3WIRO3N7.mjs} +2 -2
- package/dist/chunk-3WIRO3N7.mjs.map +1 -0
- package/dist/chunk-53MVSX3R.mjs +168 -0
- package/dist/chunk-53MVSX3R.mjs.map +1 -0
- package/dist/{chunk-AW6I64AP.mjs → chunk-6ZXIQSXE.mjs} +16 -1
- package/dist/chunk-6ZXIQSXE.mjs.map +1 -0
- package/dist/chunk-7ZVN3YZI.mjs +333 -0
- package/dist/chunk-7ZVN3YZI.mjs.map +1 -0
- package/dist/{chunk-7N62DNSO.mjs → chunk-H3TE3LOV.mjs} +2 -2
- package/dist/{chunk-QTZZ2ISQ.mjs → chunk-KLA67WKG.mjs} +1 -1
- package/dist/chunk-KLA67WKG.mjs.map +1 -0
- package/dist/{chunk-K5ZTUF22.mjs → chunk-L2PE36GQ.mjs} +7 -3
- package/dist/chunk-L2PE36GQ.mjs.map +1 -0
- package/dist/{chunk-4LND3PXF.mjs → chunk-OPHK6OJN.mjs} +513 -48
- package/dist/chunk-OPHK6OJN.mjs.map +1 -0
- package/dist/chunk-USLFXFFH.mjs +46 -0
- package/dist/chunk-USLFXFFH.mjs.map +1 -0
- package/dist/{chunk-DTOWEQRV.mjs → chunk-VSDZEB5M.mjs} +368 -103
- package/dist/chunk-VSDZEB5M.mjs.map +1 -0
- package/dist/{chunk-E2AI2CW7.mjs → chunk-Z3YG4OZT.mjs} +727 -148
- package/dist/chunk-Z3YG4OZT.mjs.map +1 -0
- package/dist/client.cjs.map +1 -1
- package/dist/client.mjs +1 -1
- package/dist/{devtools-A22KUJZT.mjs → devtools-GAZ7F3KL.mjs} +4 -3
- package/dist/{devtools-A22KUJZT.mjs.map → devtools-GAZ7F3KL.mjs.map} +1 -1
- package/dist/docs.cjs +622 -124
- package/dist/docs.cjs.map +1 -1
- package/dist/docs.mjs +5 -3
- package/dist/font.cjs +19 -2
- package/dist/font.cjs.map +1 -1
- package/dist/font.mjs +7 -3
- package/dist/index.cjs +2208 -681
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +27 -19
- package/dist/index.mjs.map +1 -1
- package/dist/internal/build-runtime.cjs +2035 -517
- package/dist/internal/build-runtime.cjs.map +1 -1
- package/dist/internal/build-runtime.mjs +13 -10
- package/dist/internal/client-runtime.cjs +169 -2
- package/dist/internal/client-runtime.cjs.map +1 -1
- package/dist/internal/client-runtime.mjs +170 -2
- package/dist/internal/client-runtime.mjs.map +1 -1
- package/dist/internal/config-runtime.cjs +22 -0
- package/dist/internal/config-runtime.cjs.map +1 -1
- package/dist/internal/config-runtime.mjs +3 -2
- package/dist/internal/metadata-image-runtime.cjs +199 -0
- package/dist/internal/metadata-image-runtime.cjs.map +1 -0
- package/dist/internal/metadata-image-runtime.mjs +8 -0
- package/dist/internal/metadata-image-runtime.mjs.map +1 -0
- package/dist/internal/production-runtime.cjs +310 -0
- package/dist/internal/production-runtime.cjs.map +1 -1
- package/dist/internal/production-runtime.mjs +10 -0
- package/dist/internal/production-runtime.mjs.map +1 -1
- package/dist/navigation.cjs.map +1 -1
- package/dist/navigation.mjs +2 -2
- package/dist/plugin.cjs +1523 -340
- package/dist/plugin.cjs.map +1 -1
- package/dist/plugin.mjs +9 -5
- package/dist/server.cjs +1636 -411
- package/dist/server.cjs.map +1 -1
- package/dist/server.mjs +17 -19
- package/dist/server.mjs.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.mjs +1 -1
- package/dist/vite.cjs +1577 -350
- package/dist/vite.cjs.map +1 -1
- package/dist/vite.mjs +9 -5
- package/package.json +10 -1
- package/dist/chunk-3QDB3IA6.mjs.map +0 -1
- package/dist/chunk-4LND3PXF.mjs.map +0 -1
- package/dist/chunk-AW6I64AP.mjs.map +0 -1
- package/dist/chunk-DTOWEQRV.mjs.map +0 -1
- package/dist/chunk-E2AI2CW7.mjs.map +0 -1
- package/dist/chunk-K5ZTUF22.mjs.map +0 -1
- package/dist/chunk-QM746KUO.mjs +0 -56
- package/dist/chunk-QM746KUO.mjs.map +0 -1
- package/dist/chunk-QTZZ2ISQ.mjs.map +0 -1
- /package/dist/{chunk-7N62DNSO.mjs.map → chunk-H3TE3LOV.mjs.map} +0 -0
|
@@ -1,27 +1,24 @@
|
|
|
1
|
+
import {
|
|
2
|
+
withProductionNodeEnv
|
|
3
|
+
} from "./chunk-4S3ORGFA.mjs";
|
|
1
4
|
import {
|
|
2
5
|
canUseRolldownForRouteDiscovery,
|
|
3
6
|
loadFarmProductionVite
|
|
4
7
|
} from "./chunk-SL6HTIOK.mjs";
|
|
5
|
-
import {
|
|
6
|
-
withProductionNodeEnv
|
|
7
|
-
} from "./chunk-4S3ORGFA.mjs";
|
|
8
8
|
import {
|
|
9
9
|
FARM_DOCS_LAST_MODIFIED_MANIFEST,
|
|
10
10
|
createFarmDocsLastModifiedManifest,
|
|
11
11
|
getFarmDocsRouteTypeEntries
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OPHK6OJN.mjs";
|
|
13
13
|
import {
|
|
14
14
|
generateFarmDocsSearchClientRuntime,
|
|
15
15
|
isFarmDocsSearchEnabled,
|
|
16
16
|
resolveFarmDocsSearchClientModule
|
|
17
17
|
} from "./chunk-V7RBCSDJ.mjs";
|
|
18
|
-
import {
|
|
19
|
-
mergeFarmViteConfig
|
|
20
|
-
} from "./chunk-QM746KUO.mjs";
|
|
21
18
|
import {
|
|
22
19
|
hasCustomFarmRouteContext,
|
|
23
20
|
resolveDeployOutputPath
|
|
24
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-L2PE36GQ.mjs";
|
|
25
22
|
import {
|
|
26
23
|
PluginManager
|
|
27
24
|
} from "./chunk-DJSGLHCN.mjs";
|
|
@@ -32,11 +29,16 @@ import {
|
|
|
32
29
|
generateFarmTypeArtifacts,
|
|
33
30
|
getClientModuleMetadata,
|
|
34
31
|
mergeFarmFontCss,
|
|
32
|
+
mergeFarmViteConfig,
|
|
35
33
|
resolveRouteRenderingConfigFromFile
|
|
36
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-Z3YG4OZT.mjs";
|
|
37
35
|
import {
|
|
38
36
|
farmEnvironmentFunctionsPlugin
|
|
39
37
|
} from "./chunk-V6WE6JSZ.mjs";
|
|
38
|
+
import {
|
|
39
|
+
resolveFarmDocsFontAssets,
|
|
40
|
+
toFarmDocsPublicFontAssets
|
|
41
|
+
} from "./chunk-USLFXFFH.mjs";
|
|
40
42
|
import {
|
|
41
43
|
routeRulesToNitroRouteRules
|
|
42
44
|
} from "./chunk-YW7OBE6Q.mjs";
|
|
@@ -319,6 +321,31 @@ function escapeRegExp(value) {
|
|
|
319
321
|
}
|
|
320
322
|
__name(escapeRegExp, "escapeRegExp");
|
|
321
323
|
|
|
324
|
+
// src/nitro/vercel-assets.ts
|
|
325
|
+
var FARM_IMMUTABLE_ASSET_CACHE_CONTROL = "public, max-age=31536000, immutable";
|
|
326
|
+
function createFarmVercelImmutableAssetRoute(basePath = "/") {
|
|
327
|
+
const normalizedBasePath = normalizeBasePath(basePath);
|
|
328
|
+
const escapedBasePath = escapeRegex(normalizedBasePath);
|
|
329
|
+
const prefix = escapedBasePath ? `/${escapedBasePath}` : "";
|
|
330
|
+
return {
|
|
331
|
+
src: `^${prefix}/(?:assets|chunks)/(?:.+/)*[^/]+-h(?:[a-fA-F0-9]{8}|[a-fA-F0-9]{12}|[a-fA-F0-9]{16})\\.(?!(?:[hH][tT][mM][lL]?)$)[^/]+$`,
|
|
332
|
+
headers: {
|
|
333
|
+
"Cache-Control": FARM_IMMUTABLE_ASSET_CACHE_CONTROL
|
|
334
|
+
},
|
|
335
|
+
continue: true,
|
|
336
|
+
caseSensitive: true
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
__name(createFarmVercelImmutableAssetRoute, "createFarmVercelImmutableAssetRoute");
|
|
340
|
+
function normalizeBasePath(basePath) {
|
|
341
|
+
return basePath.trim().replace(/^\/+|\/+$/g, "");
|
|
342
|
+
}
|
|
343
|
+
__name(normalizeBasePath, "normalizeBasePath");
|
|
344
|
+
function escapeRegex(value) {
|
|
345
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
346
|
+
}
|
|
347
|
+
__name(escapeRegex, "escapeRegex");
|
|
348
|
+
|
|
322
349
|
// src/build/vite-plugin-compat.ts
|
|
323
350
|
function adaptTailwindVitePlugin(pluginOption, builder) {
|
|
324
351
|
if (builder !== "rolldown") return pluginOption;
|
|
@@ -463,6 +490,10 @@ function isCloudflareImagePreset(preset) {
|
|
|
463
490
|
return preset === "cloudflare" || preset === "cloudflare-pages" || preset === "cloudflare-module";
|
|
464
491
|
}
|
|
465
492
|
__name(isCloudflareImagePreset, "isCloudflareImagePreset");
|
|
493
|
+
function shouldUseExternalMetadataImageRuntime(preset, hasGeneratedMetadataImages) {
|
|
494
|
+
return hasGeneratedMetadataImages && preset !== "vercel-edge" && !isCloudflareImagePreset(preset);
|
|
495
|
+
}
|
|
496
|
+
__name(shouldUseExternalMetadataImageRuntime, "shouldUseExternalMetadataImageRuntime");
|
|
466
497
|
function resolveImageRuntime(config, preset) {
|
|
467
498
|
if (config.images.provider === "none") return "none";
|
|
468
499
|
const cloudflarePreset = isCloudflareImagePreset(preset);
|
|
@@ -480,6 +511,31 @@ function isNitroRollupExternal(id) {
|
|
|
480
511
|
return NODE_BUILTIN_MODULES.has(id) || NITRO_EXTERNAL_MODULES.has(id) || normalizedId.startsWith(".prisma/") || normalizedId.includes("/node_modules/@prisma/client/") || normalizedId.includes("/node_modules/.prisma/client/");
|
|
481
512
|
}
|
|
482
513
|
__name(isNitroRollupExternal, "isNitroRollupExternal");
|
|
514
|
+
var FARM_METADATA_IMAGE_WASM_PREFIX = "\0farm-metadata-image-wasm:";
|
|
515
|
+
function createMetadataImageWasmPlugin() {
|
|
516
|
+
return {
|
|
517
|
+
name: "farm-metadata-image-wasm",
|
|
518
|
+
resolveId(source, importer) {
|
|
519
|
+
if (!source.endsWith(".wasm?module") || !importer?.replace(/\\/g, "/").includes("/@vercel/og/")) {
|
|
520
|
+
return null;
|
|
521
|
+
}
|
|
522
|
+
const requestPath = source.slice(0, -"?module".length);
|
|
523
|
+
const resolvedPath = path3.isAbsolute(requestPath) ? requestPath : path3.resolve(path3.dirname(importer.split("?", 1)[0]), requestPath);
|
|
524
|
+
return `${FARM_METADATA_IMAGE_WASM_PREFIX}${resolvedPath}`;
|
|
525
|
+
},
|
|
526
|
+
load(id) {
|
|
527
|
+
if (!id.startsWith(FARM_METADATA_IMAGE_WASM_PREFIX)) return null;
|
|
528
|
+
const wasmPath = id.slice(FARM_METADATA_IMAGE_WASM_PREFIX.length);
|
|
529
|
+
const encoded = readFileSync(wasmPath).toString("base64");
|
|
530
|
+
return `
|
|
531
|
+
const encoded = ${JSON.stringify(encoded)};
|
|
532
|
+
const bytes = Uint8Array.from(atob(encoded), (character) => character.charCodeAt(0));
|
|
533
|
+
export default new WebAssembly.Module(bytes);
|
|
534
|
+
`;
|
|
535
|
+
}
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
__name(createMetadataImageWasmPlugin, "createMetadataImageWasmPlugin");
|
|
483
539
|
function collectSSRExternalPackages(ssrBundle) {
|
|
484
540
|
const emittedFiles = new Set(
|
|
485
541
|
Object.keys(ssrBundle).map((fileName) => fileName.replace(/^\.\//, ""))
|
|
@@ -863,6 +919,9 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
|
|
|
863
919
|
}
|
|
864
920
|
const { bundle: ssrBundle, entryFile: ssrEntryFile, configuredHeaderRoutes } = ssrResult;
|
|
865
921
|
await writeSSRAssetsToClient(ssrBundle, clientOutputDir);
|
|
922
|
+
if (config.docs.enabled) {
|
|
923
|
+
await copyFarmDocsFontAssetsToClient(root, clientOutputDir);
|
|
924
|
+
}
|
|
866
925
|
logger.info(`\u{1F680} Building server with Nitro (preset: ${preset})...`);
|
|
867
926
|
await buildNitroUniversal(
|
|
868
927
|
config,
|
|
@@ -917,6 +976,17 @@ async function writeSSRAssetsToClient(bundle, outputDir) {
|
|
|
917
976
|
}
|
|
918
977
|
}
|
|
919
978
|
__name(writeSSRAssetsToClient, "writeSSRAssetsToClient");
|
|
979
|
+
async function copyFarmDocsFontAssetsToClient(root, outputDir) {
|
|
980
|
+
const fs = await import("fs/promises");
|
|
981
|
+
await Promise.all(
|
|
982
|
+
resolveFarmDocsFontAssets(root).map(async ({ sourcePath, url }) => {
|
|
983
|
+
const targetPath = path3.join(outputDir, url.replace(/^\/+/, ""));
|
|
984
|
+
await fs.mkdir(path3.dirname(targetPath), { recursive: true });
|
|
985
|
+
await fs.copyFile(sourcePath, targetPath);
|
|
986
|
+
})
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
__name(copyFarmDocsFontAssetsToClient, "copyFarmDocsFontAssetsToClient");
|
|
920
990
|
async function mergeBuiltFontCss(outputDir, fontCssSource) {
|
|
921
991
|
const fs = await import("fs/promises");
|
|
922
992
|
const fontCssPath = path3.join(outputDir, "farm-fonts.css");
|
|
@@ -935,11 +1005,8 @@ async function mergeBuiltFontCss(outputDir, fontCssSource) {
|
|
|
935
1005
|
} catch {
|
|
936
1006
|
}
|
|
937
1007
|
const normalizedFontCss = typeof fontCss === "string" ? fontCss : Buffer.from(fontCss).toString("utf8");
|
|
1008
|
+
await fs.writeFile(fontCssPath, normalizedFontCss);
|
|
938
1009
|
await fs.writeFile(clientCssPath, mergeFarmFontCss(clientCss, normalizedFontCss));
|
|
939
|
-
try {
|
|
940
|
-
await fs.rm(fontCssPath, { force: true });
|
|
941
|
-
} catch {
|
|
942
|
-
}
|
|
943
1010
|
}
|
|
944
1011
|
__name(mergeBuiltFontCss, "mergeBuiltFontCss");
|
|
945
1012
|
var _IncompleteClientBuildOutputError = class _IncompleteClientBuildOutputError extends Error {
|
|
@@ -1004,7 +1071,11 @@ async function buildClient(productionVite, config, root, srcDir, outputDir, page
|
|
|
1004
1071
|
const metadata = getClientModuleMetadata(route.modulePath, root);
|
|
1005
1072
|
if (metadata.shouldHydrate) {
|
|
1006
1073
|
const relativePath = route.modulePath.replace(root, "").replace(/^\//, "");
|
|
1007
|
-
clientPages.push({
|
|
1074
|
+
clientPages.push({
|
|
1075
|
+
...route,
|
|
1076
|
+
relativePath,
|
|
1077
|
+
islandStrategy: metadata.islandStrategy ?? "load"
|
|
1078
|
+
});
|
|
1008
1079
|
logger.info(`\u{1F4F1} Found hydratable route: ${route.pattern} -> ${route.modulePath}`);
|
|
1009
1080
|
}
|
|
1010
1081
|
} catch (error) {
|
|
@@ -1078,13 +1149,14 @@ async function buildClient(productionVite, config, root, srcDir, outputDir, page
|
|
|
1078
1149
|
},
|
|
1079
1150
|
output: {
|
|
1080
1151
|
entryFileNames: "[name].js",
|
|
1081
|
-
chunkFileNames: "chunks/[name]-[hash].js",
|
|
1152
|
+
chunkFileNames: "chunks/[name]-h[hash].js",
|
|
1153
|
+
hashCharacters: "hex",
|
|
1082
1154
|
// Use predictable name for CSS so we can reference it in SSR HTML
|
|
1083
1155
|
assetFileNames: /* @__PURE__ */ __name((assetInfo) => {
|
|
1084
1156
|
if (assetInfo.name?.endsWith(".css")) {
|
|
1085
1157
|
return "farm-client.css";
|
|
1086
1158
|
}
|
|
1087
|
-
return "assets/[name]-[hash][extname]";
|
|
1159
|
+
return "assets/[name]-h[hash][extname]";
|
|
1088
1160
|
}, "assetFileNames")
|
|
1089
1161
|
},
|
|
1090
1162
|
// Externalize Node.js built-ins and server-side modules for client build
|
|
@@ -1683,10 +1755,13 @@ document.addEventListener("click", function(e) {
|
|
|
1683
1755
|
const imports = [];
|
|
1684
1756
|
const routeEntries = [];
|
|
1685
1757
|
const routeSlotEntries = [];
|
|
1686
|
-
clientPages.forEach((page
|
|
1758
|
+
clientPages.forEach((page) => {
|
|
1687
1759
|
const importPath = toImportPath(page.modulePath);
|
|
1688
|
-
|
|
1689
|
-
|
|
1760
|
+
routeEntries.push(` {
|
|
1761
|
+
pattern: ${JSON.stringify(page.pattern)},
|
|
1762
|
+
islandStrategy: ${JSON.stringify(page.islandStrategy)},
|
|
1763
|
+
load: () => import(${JSON.stringify(importPath)}).then((module) => module.default),
|
|
1764
|
+
}`);
|
|
1690
1765
|
});
|
|
1691
1766
|
clientRouteSlots.forEach((slot, index) => {
|
|
1692
1767
|
const importPath = toImportPath(slot.modulePath);
|
|
@@ -1707,7 +1782,7 @@ ${cssImport}
|
|
|
1707
1782
|
${layoutImports}
|
|
1708
1783
|
import React from "react";
|
|
1709
1784
|
import { createRoot, hydrateRoot } from "react-dom/client";
|
|
1710
|
-
import { createClientPluginManager, installChunkErrorRecovery } from "@farm.js/core/internal/client-runtime";
|
|
1785
|
+
import { createClientPluginManager, installChunkErrorRecovery, scheduleFarmIslandHydration } from "@farm.js/core/internal/client-runtime";
|
|
1711
1786
|
import { matchFarmRoute } from "@farm.js/core/router";
|
|
1712
1787
|
${clientPluginEntry.imports}
|
|
1713
1788
|
${i18nClientRuntime}
|
|
@@ -1780,6 +1855,12 @@ function matchRoute(pathname) {
|
|
|
1780
1855
|
return null;
|
|
1781
1856
|
}
|
|
1782
1857
|
|
|
1858
|
+
async function loadRouteComponent(route) {
|
|
1859
|
+
if (route.Component) return route.Component;
|
|
1860
|
+
route.Component = await route.load();
|
|
1861
|
+
return route.Component;
|
|
1862
|
+
}
|
|
1863
|
+
|
|
1783
1864
|
function matchesRoutePrefix(pathname, pattern) {
|
|
1784
1865
|
if (pattern === "/") return true;
|
|
1785
1866
|
const pathSegments = getFarmRoutePathname(pathname).split("/").filter(Boolean);
|
|
@@ -1922,15 +2003,24 @@ function clearRouteInterception(destination) {
|
|
|
1922
2003
|
|
|
1923
2004
|
// State
|
|
1924
2005
|
let reactRoot = null;
|
|
2006
|
+
let reactRootContainer = null;
|
|
1925
2007
|
let currentPathname = null;
|
|
1926
2008
|
let isHydrated = false;
|
|
2009
|
+
let pendingPageHydrationController = null;
|
|
1927
2010
|
|
|
1928
2011
|
${generateUniversalRouterStateRuntime()}
|
|
1929
2012
|
|
|
2013
|
+
function cancelPendingPageHydration() {
|
|
2014
|
+
pendingPageHydrationController?.abort();
|
|
2015
|
+
pendingPageHydrationController = null;
|
|
2016
|
+
}
|
|
2017
|
+
|
|
1930
2018
|
function resetReactRoot() {
|
|
2019
|
+
cancelPendingPageHydration();
|
|
1931
2020
|
if (!reactRoot) return;
|
|
1932
2021
|
reactRoot.unmount();
|
|
1933
2022
|
reactRoot = null;
|
|
2023
|
+
reactRootContainer = null;
|
|
1934
2024
|
isHydrated = false;
|
|
1935
2025
|
}
|
|
1936
2026
|
|
|
@@ -1944,49 +2034,77 @@ async function hydrate() {
|
|
|
1944
2034
|
return;
|
|
1945
2035
|
}
|
|
1946
2036
|
|
|
1947
|
-
const hasRouteSlots =
|
|
1948
|
-
Array.isArray(window.__FARM_ROUTE_SLOTS__) && window.__FARM_ROUTE_SLOTS__.length > 0;
|
|
1949
2037
|
const container =
|
|
1950
|
-
|
|
2038
|
+
document.getElementById("__farm_page__") ||
|
|
1951
2039
|
document.getElementById("root");
|
|
1952
2040
|
if (!container) {
|
|
1953
2041
|
console.error("[Farm.js] No root element found");
|
|
1954
2042
|
return;
|
|
1955
2043
|
}
|
|
1956
2044
|
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
const searchParams = Object.fromEntries(new URLSearchParams(window.location.search));
|
|
1960
|
-
|
|
1961
|
-
const props = { params: params, searchParams: Promise.resolve(searchParams) };
|
|
1962
|
-
|
|
1963
|
-
// Create page element and wrap with layouts
|
|
1964
|
-
const pageElement = React.createElement(Component, props);
|
|
1965
|
-
const wrappedElement = hasRouteSlots
|
|
1966
|
-
? pageElement
|
|
1967
|
-
: wrapWithLayouts(pageElement, pathname, params);
|
|
1968
|
-
|
|
1969
|
-
const shouldHydrate = !isHydrated && Boolean(container.innerHTML.trim());
|
|
1970
|
-
const hydrationSession = await farmClientRuntime.beginHydration({
|
|
1971
|
-
container,
|
|
1972
|
-
mode: shouldHydrate ? "hydrate" : "render",
|
|
1973
|
-
});
|
|
2045
|
+
container.dataset.farmIsland = "page";
|
|
2046
|
+
container.dataset.farmIslandStrategy = matched.route.islandStrategy || "load";
|
|
1974
2047
|
|
|
2048
|
+
const hydrationController = new AbortController();
|
|
2049
|
+
pendingPageHydrationController = hydrationController;
|
|
1975
2050
|
try {
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
2051
|
+
await scheduleFarmIslandHydration({
|
|
2052
|
+
container,
|
|
2053
|
+
strategy: matched.route.islandStrategy,
|
|
2054
|
+
signal: hydrationController.signal,
|
|
2055
|
+
hydrate: async function() {
|
|
2056
|
+
const Component = await loadRouteComponent(matched.route);
|
|
2057
|
+
if (
|
|
2058
|
+
hydrationController.signal.aborted ||
|
|
2059
|
+
!container.isConnected ||
|
|
2060
|
+
window.location.pathname !== pathname
|
|
2061
|
+
) {
|
|
2062
|
+
return;
|
|
2063
|
+
}
|
|
2064
|
+
const params = matched.params;
|
|
2065
|
+
const searchParams = Object.fromEntries(new URLSearchParams(window.location.search));
|
|
2066
|
+
const props = { params: params, searchParams: Promise.resolve(searchParams) };
|
|
2067
|
+
const pageElement = React.createElement(Component, props);
|
|
2068
|
+
const wrappedElement = container.id === "__farm_page__"
|
|
2069
|
+
? pageElement
|
|
2070
|
+
: wrapWithLayouts(pageElement, pathname, params);
|
|
2071
|
+
const shouldHydrate = !isHydrated && Boolean(container.innerHTML.trim());
|
|
2072
|
+
const hydrationSession = await farmClientRuntime.beginHydration({
|
|
2073
|
+
container,
|
|
2074
|
+
mode: shouldHydrate ? "hydrate" : "render",
|
|
2075
|
+
});
|
|
2076
|
+
|
|
2077
|
+
try {
|
|
2078
|
+
if (hydrationController.signal.aborted || !container.isConnected) {
|
|
2079
|
+
await farmClientRuntime.failHydration(
|
|
2080
|
+
hydrationSession,
|
|
2081
|
+
new DOMException("Route hydration was cancelled", "AbortError"),
|
|
2082
|
+
);
|
|
2083
|
+
return;
|
|
2084
|
+
}
|
|
2085
|
+
if (shouldHydrate) {
|
|
2086
|
+
reactRoot = hydrateRoot(container, wrappedElement);
|
|
2087
|
+
reactRootContainer = container;
|
|
2088
|
+
isHydrated = true;
|
|
2089
|
+
} else {
|
|
2090
|
+
if (!reactRoot) {
|
|
2091
|
+
reactRoot = createRoot(container);
|
|
2092
|
+
reactRootContainer = container;
|
|
2093
|
+
}
|
|
2094
|
+
reactRoot.render(wrappedElement);
|
|
2095
|
+
}
|
|
2096
|
+
currentPathname = pathname;
|
|
2097
|
+
await farmClientRuntime.completeHydration(hydrationSession);
|
|
2098
|
+
} catch (error) {
|
|
2099
|
+
await farmClientRuntime.failHydration(hydrationSession, error);
|
|
2100
|
+
console.error("[Farm.js] Hydration error:", error);
|
|
2101
|
+
}
|
|
2102
|
+
},
|
|
2103
|
+
});
|
|
2104
|
+
} finally {
|
|
2105
|
+
if (pendingPageHydrationController === hydrationController) {
|
|
2106
|
+
pendingPageHydrationController = null;
|
|
1984
2107
|
}
|
|
1985
|
-
currentPathname = pathname;
|
|
1986
|
-
await farmClientRuntime.completeHydration(hydrationSession);
|
|
1987
|
-
} catch (error) {
|
|
1988
|
-
await farmClientRuntime.failHydration(hydrationSession, error);
|
|
1989
|
-
console.error("[Farm.js] Hydration error:", error);
|
|
1990
2108
|
}
|
|
1991
2109
|
}
|
|
1992
2110
|
|
|
@@ -2015,6 +2133,8 @@ ${generateUniversalRouterStateProperties()}
|
|
|
2015
2133
|
const from = this.currentPath;
|
|
2016
2134
|
if (await this.shouldBlockNavigation({ from, to, action })) return;
|
|
2017
2135
|
|
|
2136
|
+
// A route transition invalidates any trigger waiting on the previous DOM boundary.
|
|
2137
|
+
cancelPendingPageHydration();
|
|
2018
2138
|
this.saveScrollPosition(window.location.pathname);
|
|
2019
2139
|
this.startNavigation(from, url, action);
|
|
2020
2140
|
let clientNavigation;
|
|
@@ -2081,7 +2201,9 @@ ${generateUniversalRouterStateProperties()}
|
|
|
2081
2201
|
}
|
|
2082
2202
|
|
|
2083
2203
|
if (matched) {
|
|
2084
|
-
|
|
2204
|
+
// Navigation itself signals intent, so destination routes load eagerly even
|
|
2205
|
+
// when their initial document hydration strategy is deferred.
|
|
2206
|
+
const Component = await loadRouteComponent(matched.route);
|
|
2085
2207
|
const params = matched.params;
|
|
2086
2208
|
const searchParams = Object.fromEntries(url.searchParams);
|
|
2087
2209
|
const props = { params: params, searchParams: Promise.resolve(searchParams) };
|
|
@@ -2107,8 +2229,10 @@ ${generateUniversalRouterStateProperties()}
|
|
|
2107
2229
|
|
|
2108
2230
|
const container = document.getElementById("root");
|
|
2109
2231
|
if (container) {
|
|
2232
|
+
if (reactRoot && reactRootContainer !== container) resetReactRoot();
|
|
2110
2233
|
if (!reactRoot) {
|
|
2111
2234
|
reactRoot = createRoot(container);
|
|
2235
|
+
reactRootContainer = container;
|
|
2112
2236
|
}
|
|
2113
2237
|
reactRoot.render(wrappedElement);
|
|
2114
2238
|
currentPathname = pathname;
|
|
@@ -2118,7 +2242,7 @@ ${generateUniversalRouterStateProperties()}
|
|
|
2118
2242
|
const html = await this.fetchPage(url.pathname + url.search);
|
|
2119
2243
|
await farmClientRuntime.markNavigationLoaded(clientNavigation, html);
|
|
2120
2244
|
await this.runViewTransition(options.viewTransition, async () => {
|
|
2121
|
-
if (!this.swapContent(html, url.pathname + url.search)) {
|
|
2245
|
+
if (!(await this.swapContent(html, url.pathname + url.search))) {
|
|
2122
2246
|
throw new Error("Farm could not swap the target document");
|
|
2123
2247
|
}
|
|
2124
2248
|
const historyState = createHistoryState(
|
|
@@ -2173,7 +2297,7 @@ ${generateUniversalRouterStateProperties()}
|
|
|
2173
2297
|
return html;
|
|
2174
2298
|
},
|
|
2175
2299
|
|
|
2176
|
-
swapContent: function(html, targetPath) {
|
|
2300
|
+
swapContent: async function(html, targetPath) {
|
|
2177
2301
|
const parser = new DOMParser();
|
|
2178
2302
|
const doc = parser.parseFromString(html, "text/html");
|
|
2179
2303
|
if (isFarmLocaleDocumentChange(doc)) return false;
|
|
@@ -2213,18 +2337,18 @@ ${generateUniversalRouterStateProperties()}
|
|
|
2213
2337
|
const matched = matchRoute(newPathname);
|
|
2214
2338
|
if (matched) {
|
|
2215
2339
|
// Re-hydrate the client component
|
|
2216
|
-
const Component = matched.route
|
|
2340
|
+
const Component = await loadRouteComponent(matched.route);
|
|
2217
2341
|
const params = matched.params;
|
|
2218
2342
|
const searchParams = Object.fromEntries(targetUrl.searchParams);
|
|
2219
2343
|
const props = { params: params, searchParams: Promise.resolve(searchParams) };
|
|
2220
2344
|
const pageElement = React.createElement(Component, props);
|
|
2221
|
-
const hasRouteSlots = nextRouteSlots.length > 0;
|
|
2222
2345
|
const pageContainer =
|
|
2223
|
-
|
|
2224
|
-
const wrappedElement =
|
|
2346
|
+
document.getElementById("__farm_page__") || currentRoot;
|
|
2347
|
+
const wrappedElement = pageContainer.id === "__farm_page__"
|
|
2225
2348
|
? pageElement
|
|
2226
2349
|
: wrapWithLayouts(pageElement, newPathname, params);
|
|
2227
2350
|
reactRoot = hydrateRoot(pageContainer, wrappedElement);
|
|
2351
|
+
reactRootContainer = pageContainer;
|
|
2228
2352
|
isHydrated = true;
|
|
2229
2353
|
}
|
|
2230
2354
|
return true;
|
|
@@ -2515,6 +2639,10 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
|
|
|
2515
2639
|
i18nCatalogs
|
|
2516
2640
|
);
|
|
2517
2641
|
const virtualEntryId = "\0virtual:farm-ssr-entry";
|
|
2642
|
+
const useExternalMetadataImageRuntime = shouldUseExternalMetadataImageRuntime(
|
|
2643
|
+
preset,
|
|
2644
|
+
metadataImageRoutes.some((image) => image.sourceType === "module")
|
|
2645
|
+
);
|
|
2518
2646
|
try {
|
|
2519
2647
|
await viteBuild({
|
|
2520
2648
|
root,
|
|
@@ -2535,6 +2663,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
|
|
|
2535
2663
|
external: [
|
|
2536
2664
|
"fsevents",
|
|
2537
2665
|
"sharp",
|
|
2666
|
+
...useExternalMetadataImageRuntime ? ["@vercel/og"] : [],
|
|
2538
2667
|
"@prisma/client",
|
|
2539
2668
|
"@prisma/client/default",
|
|
2540
2669
|
"@prisma/client/default.js",
|
|
@@ -2563,6 +2692,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
|
|
|
2563
2692
|
external: [
|
|
2564
2693
|
"fsevents",
|
|
2565
2694
|
"sharp",
|
|
2695
|
+
...useExternalMetadataImageRuntime ? ["@vercel/og"] : [],
|
|
2566
2696
|
"esbuild",
|
|
2567
2697
|
"lightningcss",
|
|
2568
2698
|
"rollup",
|
|
@@ -2589,7 +2719,12 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
|
|
|
2589
2719
|
// React runtime. Leaving a React component package external can make it
|
|
2590
2720
|
// resolve a second React instance at runtime and break hooks during SSR.
|
|
2591
2721
|
// Native and build-only packages above remain explicitly external.
|
|
2592
|
-
noExternal: preset === "cloudflare-module" ? [
|
|
2722
|
+
noExternal: preset === "cloudflare-module" ? [
|
|
2723
|
+
"@farm.js/core",
|
|
2724
|
+
"@farm.js/core/image",
|
|
2725
|
+
"better-call",
|
|
2726
|
+
...metadataImageRoutes.some((image) => image.sourceType === "module") ? ["@vercel/og"] : []
|
|
2727
|
+
] : true
|
|
2593
2728
|
},
|
|
2594
2729
|
define: {
|
|
2595
2730
|
__FARM_ENV__: JSON.stringify(config.env || { server: {}, public: {} }),
|
|
@@ -2597,6 +2732,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
|
|
|
2597
2732
|
},
|
|
2598
2733
|
plugins: [
|
|
2599
2734
|
...tailwindVitePlugin ? [tailwindVitePlugin] : [],
|
|
2735
|
+
...!useExternalMetadataImageRuntime && metadataImageRoutes.some((image) => image.sourceType === "module") ? [createMetadataImageWasmPlugin()] : [],
|
|
2600
2736
|
{
|
|
2601
2737
|
name: "farm-react-production-mode",
|
|
2602
2738
|
enforce: "pre",
|
|
@@ -2675,8 +2811,12 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
|
|
|
2675
2811
|
__name(buildSSRInMemory, "buildSSRInMemory");
|
|
2676
2812
|
function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, routeSlots, errorRoutes, metadataImageRoutes, middlewareRoutes, redirectRoutes, configuredRewriteRoutes, configuredHeaderRoutes, notFoundPath, config, configModulePath, hasServerRuntimeIntegrations, hasRuntimeIntegrationConfig, hasConfiguredRuntimePlugins, preset, i18nCatalogs) {
|
|
2677
2813
|
const hasPluginRuntime = hasRuntimeIntegrationConfig || hasConfiguredRuntimePlugins;
|
|
2814
|
+
const hasGeneratedMetadataImages = metadataImageRoutes.some(
|
|
2815
|
+
(image) => image.sourceType === "module"
|
|
2816
|
+
);
|
|
2678
2817
|
const hasMiddlewareRuntime = middlewareRoutes.length > 0 || hasFarmMiddlewareConfig(config.middleware);
|
|
2679
2818
|
const farmDocsBaseConfig = config.docs?.enabled ? { ...config.docs, config: void 0 } : void 0;
|
|
2819
|
+
const farmDocsFontAssets = config.docs?.enabled ? toFarmDocsPublicFontAssets(resolveFarmDocsFontAssets(config.root)) : [];
|
|
2680
2820
|
const apiImports = [];
|
|
2681
2821
|
const apiRegistrations = [];
|
|
2682
2822
|
apiRoutes.forEach((route, index) => {
|
|
@@ -2715,12 +2855,13 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, routeSlot
|
|
|
2715
2855
|
return;
|
|
2716
2856
|
}
|
|
2717
2857
|
pageImports.push(`import * as ${varName} from "${route.modulePath}";`);
|
|
2718
|
-
const
|
|
2858
|
+
const clientMetadata = getClientModuleMetadata(route.modulePath, config.root);
|
|
2719
2859
|
pageRegistrations.push(`
|
|
2720
2860
|
{
|
|
2721
2861
|
pattern: ${JSON.stringify(route.pattern)},
|
|
2722
2862
|
module: ${varName},
|
|
2723
|
-
shouldHydrate: ${JSON.stringify(shouldHydrate)},
|
|
2863
|
+
shouldHydrate: ${JSON.stringify(clientMetadata.shouldHydrate)},
|
|
2864
|
+
islandStrategy: ${JSON.stringify(clientMetadata.islandStrategy)},
|
|
2724
2865
|
${route.source !== void 0 ? `markdownSource: {
|
|
2725
2866
|
source: ${JSON.stringify(route.source)},
|
|
2726
2867
|
filePath: ${JSON.stringify(route.markdownSourcePath)},
|
|
@@ -2818,16 +2959,21 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, routeSlot
|
|
|
2818
2959
|
isFarmRedirectError,
|
|
2819
2960
|
localizeFarmHref,
|
|
2820
2961
|
localizeFarmPathname,
|
|
2962
|
+
manageFarmDocumentPreloads,
|
|
2963
|
+
manageFarmLinkHeaderPreloads,
|
|
2821
2964
|
mergeMetadata,
|
|
2822
2965
|
normalizeRevalidatePath,
|
|
2966
|
+
reportFarmPreloadWarnings,
|
|
2823
2967
|
renderMetadataHead,
|
|
2824
2968
|
resolveFarmRouteContext,
|
|
2825
2969
|
stripFarmLocaleFromPathname,
|
|
2826
2970
|
withFarmRouteContext,
|
|
2827
2971
|
} from "@farm.js/core/internal/production-runtime";`;
|
|
2972
|
+
const metadataImageRuntimeImport = hasGeneratedMetadataImages ? `import { createFarmMetadataImageResponse } from "@farm.js/core/internal/metadata-image-runtime";` : "";
|
|
2828
2973
|
const pluginRuntimeImport = hasPluginRuntime ? `import { PluginManager } from "@farm.js/core/plugin";` : "";
|
|
2829
2974
|
const i18nServerImport = config.i18n.enabled ? `import { _runWithFarmI18nRequest, _setDefaultFarmI18nRuntime, createFarmI18nRuntime, getFarmI18nClientSnapshot } from "@farm.js/core/i18n/server";` : "";
|
|
2830
2975
|
const docsHandlerImport = config.docs?.enabled ? `import { createFarmDocsAPIHandler, createFarmDocsHandler, isFarmDocsAPIRequest } from "@farm.js/core/docs";` : "";
|
|
2976
|
+
const docsFontImport = config.docs?.enabled ? `import { resolveFarmLayoutFonts } from "@farm.js/core/font";` : "";
|
|
2831
2977
|
const docsRuntimeImport = config.docs?.enabled ? `import { existsSync as farmDocsExistsSync } from "node:fs";
|
|
2832
2978
|
import { dirname as farmDocsDirname, join as farmDocsJoin } from "node:path";
|
|
2833
2979
|
import { fileURLToPath as farmDocsFileURLToPath } from "node:url";` : "";
|
|
@@ -2915,9 +3061,11 @@ ${middlewareImports.join("\n")}
|
|
|
2915
3061
|
${notFoundImport}
|
|
2916
3062
|
${apiRouteHelpersImport}
|
|
2917
3063
|
${productionRuntimeImport}
|
|
3064
|
+
${metadataImageRuntimeImport}
|
|
2918
3065
|
${pluginRuntimeImport}
|
|
2919
3066
|
${i18nServerImport}
|
|
2920
3067
|
${docsHandlerImport}
|
|
3068
|
+
${docsFontImport}
|
|
2921
3069
|
${docsRuntimeImport}
|
|
2922
3070
|
${markdownHandlerImport}
|
|
2923
3071
|
${appMarkdownImport}
|
|
@@ -2929,6 +3077,8 @@ import { farmFontPreloadHeader } from "virtual:farm-font-runtime";
|
|
|
2929
3077
|
import * as React from "react";
|
|
2930
3078
|
import * as ReactDOMServer from "react-dom/server";
|
|
2931
3079
|
|
|
3080
|
+
const farmPreloadConfig = ${JSON.stringify(config.performance.preload)};
|
|
3081
|
+
|
|
2932
3082
|
// Custom 404 page component (if provided)
|
|
2933
3083
|
const hasCustomNotFound = ${notFoundPath ? "true" : "false"};
|
|
2934
3084
|
const CustomNotFoundComponent = ${notFoundPath ? "CustomNotFound.default || CustomNotFound" : "null"};
|
|
@@ -3020,7 +3170,17 @@ globalThis.__FARM_DOCS_RUNTIME_CONFIG__ = {
|
|
|
3020
3170
|
srcDir: ${JSON.stringify(config.srcDir)},
|
|
3021
3171
|
docs: farmDocsResolvedConfig,
|
|
3022
3172
|
};
|
|
3023
|
-
const farmDocsHandler = ${config.docs?.enabled ? `createFarmDocsHandler(farmDocsResolvedConfig, {
|
|
3173
|
+
const farmDocsHandler = ${config.docs?.enabled ? `createFarmDocsHandler(farmDocsResolvedConfig, {
|
|
3174
|
+
root: farmDocsRuntimeRoot,
|
|
3175
|
+
srcDir: ${JSON.stringify(config.srcDir)},
|
|
3176
|
+
clientEntry: "/farm-client.js",
|
|
3177
|
+
fontAssets: ${JSON.stringify(farmDocsFontAssets)},
|
|
3178
|
+
resolveLayoutFonts: (pathname) =>
|
|
3179
|
+
resolveFarmLayoutFonts(
|
|
3180
|
+
getApplicableLayouts(getFarmRoutePathname(pathname)).map((layout) => layout.module),
|
|
3181
|
+
),
|
|
3182
|
+
fontStylesheetHref: "/farm-fonts.css",
|
|
3183
|
+
})` : "null"};
|
|
3024
3184
|
const farmDocsAPIHandler = ${config.docs?.enabled ? `createFarmDocsAPIHandler({ rootDir: farmDocsRuntimeRoot, srcDir: ${JSON.stringify(config.srcDir)}, docs: farmDocsResolvedConfig })` : "null"};
|
|
3025
3185
|
|
|
3026
3186
|
// API routes bundled at build time
|
|
@@ -3681,30 +3841,10 @@ async function handleMetadataImageRequest(request, routePathname) {
|
|
|
3681
3841
|
? await imageModule.default(props)
|
|
3682
3842
|
: imageModule.default;
|
|
3683
3843
|
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
}
|
|
3689
|
-
|
|
3690
|
-
let body;
|
|
3691
|
-
if (typeof value === "string" || value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
|
|
3692
|
-
body = value;
|
|
3693
|
-
} else {
|
|
3694
|
-
if (!React.isValidElement(value)) {
|
|
3695
|
-
throw new Error("Metadata image must return a Response, string, bytes, or React element");
|
|
3696
|
-
}
|
|
3697
|
-
body = ReactDOMServer.renderToStaticMarkup(value);
|
|
3698
|
-
}
|
|
3699
|
-
|
|
3700
|
-
return new Response(method === "HEAD" ? null : body, {
|
|
3701
|
-
status: 200,
|
|
3702
|
-
headers: {
|
|
3703
|
-
"Content-Type": imageModule.contentType || "image/svg+xml; charset=utf-8",
|
|
3704
|
-
"Cache-Control": "public, max-age=0, must-revalidate",
|
|
3705
|
-
"X-Content-Type-Options": "nosniff",
|
|
3706
|
-
},
|
|
3707
|
-
});
|
|
3844
|
+
${hasGeneratedMetadataImages ? `return createFarmMetadataImageResponse(value, imageModule, {
|
|
3845
|
+
method,
|
|
3846
|
+
ifNoneMatch: request.headers.get("if-none-match"),
|
|
3847
|
+
});` : 'throw new Error("Generated metadata image runtime is unavailable");'}
|
|
3708
3848
|
} catch (error) {
|
|
3709
3849
|
console.error("Metadata image render failed:", error);
|
|
3710
3850
|
return new Response("Internal Server Error", {
|
|
@@ -4240,7 +4380,16 @@ async function handleFarmRequestInContext(
|
|
|
4240
4380
|
if (farmDocsHandler) {
|
|
4241
4381
|
const docsResponse = await farmDocsHandler(request.clone());
|
|
4242
4382
|
if (docsResponse) {
|
|
4243
|
-
|
|
4383
|
+
if (!docsResponse.headers.get("content-type")?.toLowerCase().includes("text/html")) {
|
|
4384
|
+
return docsResponse;
|
|
4385
|
+
}
|
|
4386
|
+
const docsHeaders = new Headers(docsResponse.headers);
|
|
4387
|
+
docsHeaders.set("x-farm-preload-buffered", "1");
|
|
4388
|
+
return new Response(docsResponse.body, {
|
|
4389
|
+
status: docsResponse.status,
|
|
4390
|
+
statusText: docsResponse.statusText,
|
|
4391
|
+
headers: docsHeaders,
|
|
4392
|
+
});
|
|
4244
4393
|
}
|
|
4245
4394
|
}
|
|
4246
4395
|
` : ""}
|
|
@@ -4368,6 +4517,7 @@ async function handleFarmRequestInContext(
|
|
|
4368
4517
|
status: 200,
|
|
4369
4518
|
headers: {
|
|
4370
4519
|
"Content-Type": "text/html; charset=utf-8",
|
|
4520
|
+
"x-farm-preload-buffered": "1",
|
|
4371
4521
|
...(farmFontPreloadHeader ? { "Link": farmFontPreloadHeader } : {}),
|
|
4372
4522
|
...getPPRHeaders("hit", pprConfig),
|
|
4373
4523
|
},
|
|
@@ -4595,10 +4745,15 @@ async function handleFarmRequestInContext(
|
|
|
4595
4745
|
pageElement = React.createElement(PageComponent, pageProps);
|
|
4596
4746
|
}
|
|
4597
4747
|
|
|
4598
|
-
if (route.shouldHydrate
|
|
4748
|
+
if (route.shouldHydrate) {
|
|
4599
4749
|
pageElement = React.createElement(
|
|
4600
4750
|
"div",
|
|
4601
|
-
{
|
|
4751
|
+
{
|
|
4752
|
+
id: "__farm_page__",
|
|
4753
|
+
"data-farm-client": "true",
|
|
4754
|
+
"data-farm-island": "page",
|
|
4755
|
+
"data-farm-island-strategy": route.islandStrategy || "load",
|
|
4756
|
+
},
|
|
4602
4757
|
pageElement,
|
|
4603
4758
|
);
|
|
4604
4759
|
}
|
|
@@ -4749,7 +4904,7 @@ async function handleFarmRequestInContext(
|
|
|
4749
4904
|
);
|
|
4750
4905
|
return applyProductionMiddlewareHeaders(new Response(streamedDocument, {
|
|
4751
4906
|
status: pageStatus,
|
|
4752
|
-
headers: responseHeaders,
|
|
4907
|
+
headers: { ...responseHeaders, "x-farm-preload-streaming": "1" },
|
|
4753
4908
|
}), middlewareHeaders);
|
|
4754
4909
|
}
|
|
4755
4910
|
|
|
@@ -4850,7 +5005,7 @@ async function handleFarmRequestInContext(
|
|
|
4850
5005
|
fullHtml,
|
|
4851
5006
|
{
|
|
4852
5007
|
status: pageStatus,
|
|
4853
|
-
headers: responseHeaders
|
|
5008
|
+
headers: { ...responseHeaders, "x-farm-preload-buffered": "1" }
|
|
4854
5009
|
}
|
|
4855
5010
|
), middlewareHeaders);
|
|
4856
5011
|
}
|
|
@@ -4954,6 +5109,7 @@ async function handleFarmRequestInContext(
|
|
|
4954
5109
|
headers: {
|
|
4955
5110
|
"Content-Type": "text/html; charset=utf-8",
|
|
4956
5111
|
"Cache-Control": "private, no-store",
|
|
5112
|
+
"x-farm-preload-buffered": "1",
|
|
4957
5113
|
},
|
|
4958
5114
|
}), middlewareHeaders);
|
|
4959
5115
|
} catch (boundaryError) {
|
|
@@ -4976,6 +5132,7 @@ async function handleFarmRequestInContext(
|
|
|
4976
5132
|
headers: {
|
|
4977
5133
|
"Content-Type": "text/html; charset=utf-8",
|
|
4978
5134
|
"Cache-Control": "private, no-store",
|
|
5135
|
+
"x-farm-preload-buffered": "1",
|
|
4979
5136
|
},
|
|
4980
5137
|
}
|
|
4981
5138
|
), middlewareHeaders);
|
|
@@ -5122,13 +5279,19 @@ async function handleFarmRequestInContext(
|
|
|
5122
5279
|
|
|
5123
5280
|
return applyProductionMiddlewareHeaders(new Response(fullHtml, {
|
|
5124
5281
|
status: 404,
|
|
5125
|
-
headers: {
|
|
5282
|
+
headers: {
|
|
5283
|
+
"Content-Type": "text/html; charset=utf-8",
|
|
5284
|
+
"x-farm-preload-buffered": "1",
|
|
5285
|
+
}
|
|
5126
5286
|
}), middlewareHeaders);
|
|
5127
5287
|
} catch (error) {
|
|
5128
5288
|
console.error("404 render error:", error);
|
|
5129
5289
|
return applyProductionMiddlewareHeaders(new Response(
|
|
5130
5290
|
\`<!DOCTYPE html><html><head><title>404</title></head><body><h1>404 - Page Not Found</h1><p>The page \${pathname} doesn't exist.</p><a href="/">Go Home</a></body></html>\`,
|
|
5131
|
-
{
|
|
5291
|
+
{
|
|
5292
|
+
status: 404,
|
|
5293
|
+
headers: { "Content-Type": "text/html", "x-farm-preload-buffered": "1" },
|
|
5294
|
+
}
|
|
5132
5295
|
), middlewareHeaders);
|
|
5133
5296
|
}
|
|
5134
5297
|
${hasMiddlewareRuntime ? `
|
|
@@ -5170,7 +5333,10 @@ async function handleFarmPluginRequest(request, runtimeOptions) {
|
|
|
5170
5333
|
await farmPluginRuntime.runHookParallel("beforeRender", renderPayload);
|
|
5171
5334
|
|
|
5172
5335
|
const response = await handleFarmRequest(request);
|
|
5173
|
-
if (
|
|
5336
|
+
if (
|
|
5337
|
+
!hasFarmPluginHTMLTransforms ||
|
|
5338
|
+
!response.headers.get("content-type")?.toLowerCase().includes("text/html")
|
|
5339
|
+
) {
|
|
5174
5340
|
return response;
|
|
5175
5341
|
}
|
|
5176
5342
|
|
|
@@ -5180,6 +5346,8 @@ async function handleFarmPluginRequest(request, runtimeOptions) {
|
|
|
5180
5346
|
|
|
5181
5347
|
const headers = new Headers(response.headers);
|
|
5182
5348
|
headers.delete("content-length");
|
|
5349
|
+
headers.delete("x-farm-preload-streaming");
|
|
5350
|
+
headers.set("x-farm-preload-buffered", "1");
|
|
5183
5351
|
return new Response(html, {
|
|
5184
5352
|
status: response.status,
|
|
5185
5353
|
statusText: response.statusText,
|
|
@@ -5187,6 +5355,49 @@ async function handleFarmPluginRequest(request, runtimeOptions) {
|
|
|
5187
5355
|
});
|
|
5188
5356
|
}
|
|
5189
5357
|
|
|
5358
|
+
async function applyFarmPreloadBudget(response, pathname) {
|
|
5359
|
+
const headers = new Headers(response.headers);
|
|
5360
|
+
const linkHeader = headers.get("Link") || "";
|
|
5361
|
+
const isHtml = headers.get("Content-Type")?.toLowerCase().includes("text/html");
|
|
5362
|
+
const isStreaming = headers.get("x-farm-preload-streaming") === "1";
|
|
5363
|
+
const isBuffered = headers.get("x-farm-preload-buffered") === "1";
|
|
5364
|
+
headers.delete("x-farm-preload-streaming");
|
|
5365
|
+
headers.delete("x-farm-preload-buffered");
|
|
5366
|
+
|
|
5367
|
+
if (!isHtml) {
|
|
5368
|
+
if (!isStreaming && !isBuffered) return response;
|
|
5369
|
+
return new Response(response.body, {
|
|
5370
|
+
status: response.status,
|
|
5371
|
+
statusText: response.statusText,
|
|
5372
|
+
headers,
|
|
5373
|
+
});
|
|
5374
|
+
}
|
|
5375
|
+
|
|
5376
|
+
if (isStreaming || !isBuffered || response.body === null) {
|
|
5377
|
+
const managed = manageFarmLinkHeaderPreloads(linkHeader, farmPreloadConfig);
|
|
5378
|
+
if (managed.value) headers.set("Link", managed.value);
|
|
5379
|
+
else headers.delete("Link");
|
|
5380
|
+
reportFarmPreloadWarnings(managed.warnings, "route " + pathname);
|
|
5381
|
+
return new Response(response.body, {
|
|
5382
|
+
status: response.status,
|
|
5383
|
+
statusText: response.statusText,
|
|
5384
|
+
headers,
|
|
5385
|
+
});
|
|
5386
|
+
}
|
|
5387
|
+
|
|
5388
|
+
const html = await response.text();
|
|
5389
|
+
const managed = manageFarmDocumentPreloads(html, linkHeader, farmPreloadConfig);
|
|
5390
|
+
if (managed.linkHeader) headers.set("Link", managed.linkHeader);
|
|
5391
|
+
else headers.delete("Link");
|
|
5392
|
+
if (managed.html !== html) headers.delete("Content-Length");
|
|
5393
|
+
reportFarmPreloadWarnings(managed.warnings, "route " + pathname);
|
|
5394
|
+
return new Response(managed.html, {
|
|
5395
|
+
status: response.status,
|
|
5396
|
+
statusText: response.statusText,
|
|
5397
|
+
headers,
|
|
5398
|
+
});
|
|
5399
|
+
}
|
|
5400
|
+
|
|
5190
5401
|
// Export as Web Standard fetch API
|
|
5191
5402
|
export async function fetch(request, context) {
|
|
5192
5403
|
const runtimeOptions = farmPluginRuntime ? getFarmPluginRequestOptions(request) : null;
|
|
@@ -5209,7 +5420,8 @@ export async function fetch(request, context) {
|
|
|
5209
5420
|
const response = await _runWithCurrentRequest(request, () =>
|
|
5210
5421
|
_runWithAfterRequest(request, runRequest, context),
|
|
5211
5422
|
);
|
|
5212
|
-
|
|
5423
|
+
const pathname = new URL(request.url).pathname;
|
|
5424
|
+
return applyFarmPreloadBudget(applyConfiguredResponseHeaders(response, pathname), pathname);
|
|
5213
5425
|
}
|
|
5214
5426
|
export default { fetch };
|
|
5215
5427
|
`.trim();
|
|
@@ -5365,8 +5577,19 @@ async function buildNitroUniversal(config, routeManager, apiRouteManager, server
|
|
|
5365
5577
|
const outputDir = resolveDeployOutputPath(root, config.deploy.outputDir);
|
|
5366
5578
|
const ssrOutputDir = path3.join(root, distDir, "ssr");
|
|
5367
5579
|
const imageRuntime = resolveImageRuntime(config, preset);
|
|
5580
|
+
const hasGeneratedMetadataImages = Array.from(routeManager.getMetadataImages().values()).some(
|
|
5581
|
+
(image) => image.sourceType === "module"
|
|
5582
|
+
);
|
|
5583
|
+
const useExternalMetadataImageRuntime = shouldUseExternalMetadataImageRuntime(
|
|
5584
|
+
preset,
|
|
5585
|
+
hasGeneratedMetadataImages
|
|
5586
|
+
);
|
|
5587
|
+
const nitroRollupExternal = /* @__PURE__ */ __name((id) => useExternalMetadataImageRuntime && id === "@vercel/og" || isNitroRollupExternal(id), "nitroRollupExternal");
|
|
5368
5588
|
const ssrExternalPackages = collectSSRExternalPackages(ssrBundle);
|
|
5369
|
-
const copiedRuntimePackages = new Set(
|
|
5589
|
+
const copiedRuntimePackages = /* @__PURE__ */ new Set([
|
|
5590
|
+
...imageRuntime === "node" ? ["sharp"] : [],
|
|
5591
|
+
...useExternalMetadataImageRuntime ? ["@vercel/og"] : []
|
|
5592
|
+
]);
|
|
5370
5593
|
const isPrebuiltSSRCandidate = preset === "node-server" && [...ssrExternalPackages].every((packageName) => copiedRuntimePackages.has(packageName));
|
|
5371
5594
|
logger.info(`\u{1F4E6} Nitro output directory: ${outputDir}`);
|
|
5372
5595
|
logger.info(`\u{1F4E6} SSR entry file: ${ssrEntryFile}`);
|
|
@@ -5582,11 +5805,13 @@ export default async function farmNitroEventHandler(event) {
|
|
|
5582
5805
|
".prisma/client",
|
|
5583
5806
|
".prisma/client/default",
|
|
5584
5807
|
"better-sqlite3",
|
|
5585
|
-
"sharp"
|
|
5808
|
+
"sharp",
|
|
5809
|
+
...useExternalMetadataImageRuntime ? ["@vercel/og"] : []
|
|
5586
5810
|
]
|
|
5587
5811
|
},
|
|
5588
5812
|
rollupConfig: {
|
|
5589
|
-
external:
|
|
5813
|
+
external: nitroRollupExternal,
|
|
5814
|
+
plugins: hasGeneratedMetadataImages && !useExternalMetadataImageRuntime ? [createMetadataImageWasmPlugin()] : []
|
|
5590
5815
|
},
|
|
5591
5816
|
// Keep the public Nitro boolean intact for build hooks. It is translated to
|
|
5592
5817
|
// Nitro's existing esbuild pass after hooks run, avoiding a second Terser pass.
|
|
@@ -5614,7 +5839,7 @@ export default async function farmNitroEventHandler(event) {
|
|
|
5614
5839
|
snapshotSSRRebundleOptions(nitroConfig)
|
|
5615
5840
|
);
|
|
5616
5841
|
const requestedBuilder = nitroConfig.builder ?? process.env.NITRO_BUILDER;
|
|
5617
|
-
const canReusePrebuiltSSR = isPrebuiltSSRCandidate && nitroConfig.preset === "node-server" && (requestedBuilder === void 0 || requestedBuilder === "rollup" || requestedBuilder === "rolldown") && nitroConfig.sourceMap === false && !hasUnsupportedEsbuildOverrides && !hasLateBuildMutationConfig && !hasUnsupportedSSRRebundleOverrides && nitroConfig.rollupConfig?.external ===
|
|
5842
|
+
const canReusePrebuiltSSR = isPrebuiltSSRCandidate && nitroConfig.preset === "node-server" && (requestedBuilder === void 0 || requestedBuilder === "rollup" || requestedBuilder === "rolldown") && nitroConfig.sourceMap === false && !hasUnsupportedEsbuildOverrides && !hasLateBuildMutationConfig && !hasUnsupportedSSRRebundleOverrides && nitroConfig.rollupConfig?.external === nitroRollupExternal && !hasRollupPlugins(nitroConfig.rollupConfig?.plugins) && customRollupKeys.length === 0 && path3.resolve(nitroConfig.output?.serverDir || "") === path3.resolve(expectedServerDir) && Boolean(
|
|
5618
5843
|
nitroConfig.handlers?.some(
|
|
5619
5844
|
(handler) => path3.resolve(handler?.handler || "") === nitroEntryPath
|
|
5620
5845
|
)
|
|
@@ -5702,7 +5927,8 @@ export default async function farmNitroEventHandler(event) {
|
|
|
5702
5927
|
effectiveMinify,
|
|
5703
5928
|
fs
|
|
5704
5929
|
) : Promise.resolve(),
|
|
5705
|
-
imageRuntime === "node" ? copySharpRuntime(config, root, path3.join(outputDir, "server"), fs) : Promise.resolve()
|
|
5930
|
+
imageRuntime === "node" ? copySharpRuntime(config, root, path3.join(outputDir, "server"), fs) : Promise.resolve(),
|
|
5931
|
+
useExternalMetadataImageRuntime ? copyMetadataImageRuntime(root, path3.join(outputDir, "server"), fs) : Promise.resolve()
|
|
5706
5932
|
]);
|
|
5707
5933
|
const failedCopy = copyResults.find(
|
|
5708
5934
|
(result) => result.status === "rejected"
|
|
@@ -5814,6 +6040,9 @@ async function postProcessVercelOutput(root, outputDir, fs, config, farmWorkflow
|
|
|
5814
6040
|
fs
|
|
5815
6041
|
);
|
|
5816
6042
|
vercelConfig.routes = [
|
|
6043
|
+
// Apply the header before the filesystem handler. `continue` lets Vercel
|
|
6044
|
+
// serve the matching file while preserving the immutable cache policy.
|
|
6045
|
+
createFarmVercelImmutableAssetRoute(config.basePath),
|
|
5817
6046
|
// Serve static files first
|
|
5818
6047
|
{
|
|
5819
6048
|
handle: "filesystem"
|
|
@@ -5931,6 +6160,42 @@ async function copySharpRuntime(config, root, nitroFuncDir, fs) {
|
|
|
5931
6160
|
logger.info(`\u{1F5BC}\uFE0F Bundled Sharp image runtime (${copiedPackages.size} packages)`);
|
|
5932
6161
|
}
|
|
5933
6162
|
__name(copySharpRuntime, "copySharpRuntime");
|
|
6163
|
+
async function copyMetadataImageRuntime(root, nitroFuncDir, fs) {
|
|
6164
|
+
const projectRequire = createRequire(path3.join(root, "package.json"));
|
|
6165
|
+
let runtimeRequire = projectRequire;
|
|
6166
|
+
let packageJsonPath = resolvePackageJson(runtimeRequire, "@vercel/og");
|
|
6167
|
+
if (!packageJsonPath) {
|
|
6168
|
+
try {
|
|
6169
|
+
runtimeRequire = createRequire(projectRequire.resolve("@farm.js/core"));
|
|
6170
|
+
packageJsonPath = resolvePackageJson(runtimeRequire, "@vercel/og");
|
|
6171
|
+
} catch {
|
|
6172
|
+
}
|
|
6173
|
+
}
|
|
6174
|
+
if (!packageJsonPath) {
|
|
6175
|
+
throw new Error(
|
|
6176
|
+
"Generated metadata images require @vercel/og. Reinstall @farm.js/core with production dependencies."
|
|
6177
|
+
);
|
|
6178
|
+
}
|
|
6179
|
+
const packageJson = JSON.parse(await fs.readFile(packageJsonPath, "utf8"));
|
|
6180
|
+
const packageDir = path3.dirname(packageJsonPath);
|
|
6181
|
+
const targetDir = path3.join(nitroFuncDir, "node_modules", "@vercel", "og");
|
|
6182
|
+
await fs.mkdir(path3.dirname(targetDir), { recursive: true });
|
|
6183
|
+
await fs.cp(packageDir, targetDir, {
|
|
6184
|
+
recursive: true,
|
|
6185
|
+
force: true,
|
|
6186
|
+
dereference: true,
|
|
6187
|
+
mode: fsConstants.COPYFILE_FICLONE
|
|
6188
|
+
});
|
|
6189
|
+
const functionPackagePath = path3.join(nitroFuncDir, "package.json");
|
|
6190
|
+
const functionPackage = JSON.parse(await fs.readFile(functionPackagePath, "utf8"));
|
|
6191
|
+
functionPackage.dependencies = {
|
|
6192
|
+
...functionPackage.dependencies,
|
|
6193
|
+
"@vercel/og": String(packageJson.version || "0.11.1")
|
|
6194
|
+
};
|
|
6195
|
+
await fs.writeFile(functionPackagePath, JSON.stringify(functionPackage, null, 2));
|
|
6196
|
+
logger.info("\u{1F5BC}\uFE0F Bundled generated metadata image runtime");
|
|
6197
|
+
}
|
|
6198
|
+
__name(copyMetadataImageRuntime, "copyMetadataImageRuntime");
|
|
5934
6199
|
function resolvePackageJson(parentRequire, packageName) {
|
|
5935
6200
|
for (const request of [`${packageName}/package.json`, `${packageName}/package`]) {
|
|
5936
6201
|
try {
|
|
@@ -6320,4 +6585,4 @@ __name(buildNitro, "buildNitro");
|
|
|
6320
6585
|
export {
|
|
6321
6586
|
build
|
|
6322
6587
|
};
|
|
6323
|
-
//# sourceMappingURL=chunk-
|
|
6588
|
+
//# sourceMappingURL=chunk-VSDZEB5M.mjs.map
|