@reckona/mreact-router 0.0.120 → 0.0.121

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/src/build.ts CHANGED
@@ -39,9 +39,7 @@ import {
39
39
  } from "./module-runner.js";
40
40
  import { scanAppRoutes } from "./routes.js";
41
41
  import type { AppRoute } from "./routes.js";
42
- import {
43
- appFileConventionForRootFilename,
44
- } from "./file-conventions.js";
42
+ import { appFileConventionForRootFilename } from "./file-conventions.js";
45
43
  import {
46
44
  resolveAppRouterProjectOptions,
47
45
  resolveBuildTargets,
@@ -210,6 +208,7 @@ export interface BuiltRouteSourceAnalysisSummary {
210
208
  authIncludesClaims: boolean;
211
209
  cachePolicy?: RouteCachePolicy | undefined;
212
210
  clientBoundaryImports: readonly string[];
211
+ clientBoundaryFallbackImports: readonly string[];
213
212
  clientRoute: boolean;
214
213
  hasLoader: boolean;
215
214
  routeCode: string;
@@ -233,6 +232,7 @@ interface BuildSourceAnalysis {
233
232
 
234
233
  interface BuildRouteSourceAnalysis extends BuildSourceAnalysis {
235
234
  clientBoundaryImports: readonly string[];
235
+ clientBoundaryFallbackImports: readonly string[];
236
236
  clientRoute: boolean;
237
237
  file: string;
238
238
  route: AppRoute & { kind: "page" };
@@ -428,7 +428,11 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
428
428
  }));
429
429
  const [serverModuleArtifacts, clientBundle] = await Promise.all([
430
430
  timingSink === undefined
431
- ? writeServerModuleArtifactFiles(serverDir, serverModules, generatedImportPolicy.runtimePackages)
431
+ ? writeServerModuleArtifactFiles(
432
+ serverDir,
433
+ serverModules,
434
+ generatedImportPolicy.runtimePackages,
435
+ )
432
436
  : timeBuildPhase(timingSink, "serverModuleArtifacts", () =>
433
437
  writeServerModuleArtifactFiles(
434
438
  serverDir,
@@ -743,6 +747,7 @@ async function analyzeBuildRouteSources(options: {
743
747
  {
744
748
  ...analyzeBuildSource(source, route.file),
745
749
  clientBoundaryImports: clientInference.clientBoundaryImports,
750
+ clientBoundaryFallbackImports: clientInference.clientBoundaryFallbackImports,
746
751
  clientRoute: clientInference.client,
747
752
  file,
748
753
  route,
@@ -1204,7 +1209,11 @@ async function collectRuntimeOptionalPackages(options: {
1204
1209
  { packageName: options.packageName, optional: false, startDir: options.projectRoot },
1205
1210
  ];
1206
1211
 
1207
- for (let index = 0; index < queue.length && seenPackageJson.size < maxRuntimePackageManifestReads; index += 1) {
1212
+ for (
1213
+ let index = 0;
1214
+ index < queue.length && seenPackageJson.size < maxRuntimePackageManifestReads;
1215
+ index += 1
1216
+ ) {
1208
1217
  const item = queue[index];
1209
1218
  if (item === undefined || !isValidRuntimePackageName(item.packageName)) {
1210
1219
  continue;
@@ -1300,7 +1309,11 @@ async function findRuntimePackageJson(
1300
1309
  }
1301
1310
 
1302
1311
  function isRuntimePackageSpecifier(specifier: string): boolean {
1303
- if (specifier.startsWith(".") || specifier.startsWith("/") || /^[A-Za-z][A-Za-z0-9+.-]*:/.test(specifier)) {
1312
+ if (
1313
+ specifier.startsWith(".") ||
1314
+ specifier.startsWith("/") ||
1315
+ /^[A-Za-z][A-Za-z0-9+.-]*:/.test(specifier)
1316
+ ) {
1304
1317
  return false;
1305
1318
  }
1306
1319
 
@@ -1428,7 +1441,11 @@ function isSourceModuleFile(file: string): boolean {
1428
1441
  }
1429
1442
 
1430
1443
  function isAppRelativeFile(file: string, relativeRoutesDir: string): boolean {
1431
- return relativeRoutesDir === "" || file === relativeRoutesDir || file.startsWith(`${relativeRoutesDir}/`);
1444
+ return (
1445
+ relativeRoutesDir === "" ||
1446
+ file === relativeRoutesDir ||
1447
+ file.startsWith(`${relativeRoutesDir}/`)
1448
+ );
1432
1449
  }
1433
1450
 
1434
1451
  function moduleIdForBuildFile(file: string, relativeRoutesDir: string): string {
@@ -1468,10 +1485,7 @@ async function collectPublicAssetPaths(publicDir: string): Promise<string[]> {
1468
1485
  return paths.sort();
1469
1486
  }
1470
1487
 
1471
- async function copyAppFileConventionAssets(
1472
- appDir: string,
1473
- outDir: string,
1474
- ): Promise<string[]> {
1488
+ async function copyAppFileConventionAssets(appDir: string, outDir: string): Promise<string[]> {
1475
1489
  let entries: Dirent[];
1476
1490
  try {
1477
1491
  entries = await readdir(appDir, { withFileTypes: true });
@@ -1496,9 +1510,7 @@ async function copyAppFileConventionAssets(
1496
1510
  continue;
1497
1511
  }
1498
1512
 
1499
- const outputPath = convention.path.startsWith("/")
1500
- ? convention.path.slice(1)
1501
- : convention.path;
1513
+ const outputPath = convention.path.startsWith("/") ? convention.path.slice(1) : convention.path;
1502
1514
  await copyFile(join(appDir, entry.name), join(outDir, outputPath));
1503
1515
  paths.push(convention.path);
1504
1516
  }
@@ -1748,205 +1760,219 @@ async function buildServerModuleArtifacts(options: {
1748
1760
  }
1749
1761
  }
1750
1762
 
1751
- const requestBatchOutputs = requestBatchEntries.length >= 3
1752
- ? await bundleRouteRequestModuleBatchCode({
1753
- appDir: options.project.routesDir,
1754
- bundleCache: options.bundleCache,
1755
- cacheDir: options.cacheDir,
1756
- entries: requestBatchEntries,
1757
- importPolicy: requestModuleImportPolicy,
1758
- vitePlugins: options.vitePlugins,
1759
- })
1760
- : new Map<string, string>();
1761
-
1762
- const artifactEntries = await mapWithBuildConcurrency(Object.entries(options.files), async ([file, source]) => {
1763
- const absoluteFile = join(options.projectRoot, file);
1764
- const route = routeByFile.get(file);
1765
- const routeAnalysis = options.sourceAnalysis.byRouteFile.get(file);
1766
- const artifact: BuiltServerModuleArtifact = {};
1763
+ const requestBatchOutputs =
1764
+ requestBatchEntries.length >= 3
1765
+ ? await bundleRouteRequestModuleBatchCode({
1766
+ appDir: options.project.routesDir,
1767
+ bundleCache: options.bundleCache,
1768
+ cacheDir: options.cacheDir,
1769
+ entries: requestBatchEntries,
1770
+ importPolicy: requestModuleImportPolicy,
1771
+ vitePlugins: options.vitePlugins,
1772
+ })
1773
+ : new Map<string, string>();
1767
1774
 
1768
- if (
1769
- requestArtifactFiles.has(file) ||
1770
- loaderArtifactFiles.has(file) ||
1771
- metadataArtifactFiles.has(file)
1772
- ) {
1773
- if (loaderArtifactFiles.has(file)) {
1774
- const code = requestBatchOutputs.get(routeRequestArtifactBatchKey(file, "loader")) ??
1775
- await bundleRouteLoaderModuleCode({
1776
- appDir: options.project.routesDir,
1777
- bundleCache: options.bundleCache,
1778
- cacheDir: options.cacheDir,
1779
- code: stripRouteLoaderOnlyExports(source, absoluteFile),
1780
- filename: absoluteFile,
1781
- importPolicy: requestModuleImportPolicy,
1782
- vitePlugins: options.vitePlugins,
1783
- });
1784
- artifact.loader = {
1785
- code,
1786
- sourceHash: hashText(source),
1787
- };
1788
- }
1775
+ const artifactEntries = await mapWithBuildConcurrency(
1776
+ Object.entries(options.files),
1777
+ async ([file, source]) => {
1778
+ const absoluteFile = join(options.projectRoot, file);
1779
+ const route = routeByFile.get(file);
1780
+ const routeAnalysis = options.sourceAnalysis.byRouteFile.get(file);
1781
+ const artifact: BuiltServerModuleArtifact = {};
1789
1782
 
1790
- if (metadataArtifactFiles.has(file)) {
1791
- const code = requestBatchOutputs.get(routeRequestArtifactBatchKey(file, "metadata")) ??
1792
- await bundleRouteRequestModuleCode({
1793
- appDir: options.project.routesDir,
1794
- bundleCache: options.bundleCache,
1795
- cacheDir: options.cacheDir,
1796
- code: stripRouteMetadataOnlyExports(source, absoluteFile),
1797
- filename: absoluteFile,
1798
- importPolicy: requestModuleImportPolicy,
1799
- label: "Metadata",
1800
- vitePlugins: options.vitePlugins,
1801
- });
1802
- artifact.routeMetadata = {
1803
- code,
1804
- sourceHash: hashText(source),
1805
- };
1806
- }
1783
+ if (
1784
+ requestArtifactFiles.has(file) ||
1785
+ loaderArtifactFiles.has(file) ||
1786
+ metadataArtifactFiles.has(file)
1787
+ ) {
1788
+ if (loaderArtifactFiles.has(file)) {
1789
+ const code =
1790
+ requestBatchOutputs.get(routeRequestArtifactBatchKey(file, "loader")) ??
1791
+ (await bundleRouteLoaderModuleCode({
1792
+ appDir: options.project.routesDir,
1793
+ bundleCache: options.bundleCache,
1794
+ cacheDir: options.cacheDir,
1795
+ code: stripRouteLoaderOnlyExports(source, absoluteFile),
1796
+ filename: absoluteFile,
1797
+ importPolicy: requestModuleImportPolicy,
1798
+ vitePlugins: options.vitePlugins,
1799
+ }));
1800
+ artifact.loader = {
1801
+ code,
1802
+ sourceHash: hashText(source),
1803
+ };
1804
+ }
1807
1805
 
1808
- if (requestArtifactFiles.has(file)) {
1809
- const batchedRequestCode = requestBatchOutputs.get(
1810
- routeRequestArtifactBatchKey(file, "request"),
1811
- );
1812
- artifact.request = {
1813
- code: batchedRequestCode ??
1814
- await buildRequestModuleArtifactCode({
1806
+ if (metadataArtifactFiles.has(file)) {
1807
+ const code =
1808
+ requestBatchOutputs.get(routeRequestArtifactBatchKey(file, "metadata")) ??
1809
+ (await bundleRouteRequestModuleCode({
1815
1810
  appDir: options.project.routesDir,
1816
1811
  bundleCache: options.bundleCache,
1817
1812
  cacheDir: options.cacheDir,
1813
+ code: stripRouteMetadataOnlyExports(source, absoluteFile),
1818
1814
  filename: absoluteFile,
1819
1815
  importPolicy: requestModuleImportPolicy,
1820
- routeKind: route?.kind,
1821
- source,
1816
+ label: "Metadata",
1822
1817
  vitePlugins: options.vitePlugins,
1823
- }),
1824
- sourceHash: hashText(source),
1825
- };
1818
+ }));
1819
+ artifact.routeMetadata = {
1820
+ code,
1821
+ sourceHash: hashText(source),
1822
+ };
1823
+ }
1824
+
1825
+ if (requestArtifactFiles.has(file)) {
1826
+ const batchedRequestCode = requestBatchOutputs.get(
1827
+ routeRequestArtifactBatchKey(file, "request"),
1828
+ );
1829
+ artifact.request = {
1830
+ code:
1831
+ batchedRequestCode ??
1832
+ (await buildRequestModuleArtifactCode({
1833
+ appDir: options.project.routesDir,
1834
+ bundleCache: options.bundleCache,
1835
+ cacheDir: options.cacheDir,
1836
+ filename: absoluteFile,
1837
+ importPolicy: requestModuleImportPolicy,
1838
+ routeKind: route?.kind,
1839
+ source,
1840
+ vitePlugins: options.vitePlugins,
1841
+ })),
1842
+ sourceHash: hashText(source),
1843
+ };
1844
+ }
1826
1845
  }
1827
- }
1828
1846
 
1829
- if (!isServerComponentFile(file)) {
1830
- return Object.keys(artifact).length > 0 ? ([file, artifact] as const) : undefined;
1831
- }
1847
+ if (!isServerComponentFile(file)) {
1848
+ return Object.keys(artifact).length > 0 ? ([file, artifact] as const) : undefined;
1849
+ }
1832
1850
 
1833
- const closureUsesAwait = routeAnalysis?.streamRoute ??
1834
- shouldBuildRouteAsStream({
1835
- filename: file,
1836
- files: options.files,
1837
- projectRoot: options.projectRoot,
1838
- source,
1839
- });
1840
- const streamRoute = route !== undefined && closureUsesAwait;
1841
- const serverOutputs =
1842
- streamRoute || (route === undefined && closureUsesAwait)
1843
- ? (["stream", "string"] as const)
1844
- : (["string"] as const);
1845
- const code = routeAnalysis?.routeCode ??
1846
- (route === undefined ? source : stripRouteBuildExports(source, absoluteFile));
1847
- const clientInference = routeAnalysis === undefined
1848
- ? await inferClientRouteModule({
1849
- ...(route === undefined ? {} : { appDir: options.project.routesDir }),
1850
- cache: options.clientRouteInferenceCache,
1851
- code:
1852
- route === undefined
1853
- ? stripRouteClientOnlyExports(source, absoluteFile)
1854
- : stripRouteClientSource({ code: source, filename: route.file }),
1855
- filename: join(options.projectRoot, file),
1856
- ...(route === undefined ? {} : { routePath: route.path }),
1857
- vitePlugins: options.vitePlugins,
1858
- })
1859
- : undefined;
1860
- const clientBoundaryImports = routeAnalysis?.clientBoundaryImports ??
1861
- clientInference?.clientBoundaryImports ??
1862
- [];
1851
+ const closureUsesAwait =
1852
+ routeAnalysis?.streamRoute ??
1853
+ shouldBuildRouteAsStream({
1854
+ filename: file,
1855
+ files: options.files,
1856
+ projectRoot: options.projectRoot,
1857
+ source,
1858
+ });
1859
+ const streamRoute = route !== undefined && closureUsesAwait;
1860
+ const serverOutputs =
1861
+ streamRoute || (route === undefined && closureUsesAwait)
1862
+ ? (["stream", "string"] as const)
1863
+ : (["string"] as const);
1864
+ const code =
1865
+ routeAnalysis?.routeCode ??
1866
+ (route === undefined ? source : stripRouteBuildExports(source, absoluteFile));
1867
+ const clientInference =
1868
+ routeAnalysis === undefined
1869
+ ? await inferClientRouteModule({
1870
+ ...(route === undefined ? {} : { appDir: options.project.routesDir }),
1871
+ cache: options.clientRouteInferenceCache,
1872
+ code:
1873
+ route === undefined
1874
+ ? stripRouteClientOnlyExports(source, absoluteFile)
1875
+ : stripRouteClientSource({ code: source, filename: route.file }),
1876
+ filename: join(options.projectRoot, file),
1877
+ ...(route === undefined ? {} : { routePath: route.path }),
1878
+ vitePlugins: options.vitePlugins,
1879
+ })
1880
+ : undefined;
1881
+ const clientBoundaryImports =
1882
+ routeAnalysis?.clientBoundaryImports ?? clientInference?.clientBoundaryImports ?? [];
1883
+ const clientBoundaryFallbackImports =
1884
+ routeAnalysis?.clientBoundaryFallbackImports ??
1885
+ clientInference?.clientBoundaryFallbackImports ??
1886
+ [];
1887
+
1888
+ for (const diagnostic of clientInference?.diagnostics ?? []) {
1889
+ console.warn(formatClientRouteInferenceDiagnostic(diagnostic));
1890
+ }
1863
1891
 
1864
- for (const diagnostic of clientInference?.diagnostics ?? []) {
1865
- console.warn(formatClientRouteInferenceDiagnostic(diagnostic));
1866
- }
1892
+ if (routeAnalysis !== undefined) {
1893
+ artifact.analysis = builtRouteSourceAnalysisSummary({
1894
+ analysis: routeAnalysis,
1895
+ });
1896
+ }
1867
1897
 
1868
- if (routeAnalysis !== undefined) {
1869
- artifact.analysis = builtRouteSourceAnalysisSummary({
1870
- analysis: routeAnalysis,
1871
- });
1872
- }
1898
+ const serverOutputArtifacts = await mapWithBuildConcurrency(
1899
+ serverOutputs,
1900
+ async (serverOutput) => {
1901
+ const output = await transformServerRouteSource({
1902
+ cache: options.serverTransformCache,
1903
+ code,
1904
+ clientBoundaryImports,
1905
+ clientBoundaryFallbackImports,
1906
+ filename: join(options.projectRoot, file),
1907
+ moduleContextCache: options.clientRouteInferenceCache,
1908
+ serverOutput,
1909
+ });
1910
+ const fatalDiagnostics = output.diagnostics.filter(
1911
+ (diagnostic) => diagnostic.code !== "MR_UNSUPPORTED_SERVER_EVENT_HANDLER",
1912
+ );
1873
1913
 
1874
- const serverOutputArtifacts = await mapWithBuildConcurrency(
1875
- serverOutputs,
1876
- async (serverOutput) => {
1877
- const output = await transformServerRouteSource({
1878
- cache: options.serverTransformCache,
1879
- code,
1880
- clientBoundaryImports,
1881
- filename: join(options.projectRoot, file),
1882
- moduleContextCache: options.clientRouteInferenceCache,
1883
- serverOutput,
1884
- });
1885
- const fatalDiagnostics = output.diagnostics.filter(
1886
- (diagnostic) => diagnostic.code !== "MR_UNSUPPORTED_SERVER_EVENT_HANDLER",
1887
- );
1914
+ if (fatalDiagnostics.length > 0) {
1915
+ if (
1916
+ serverOutput === "string" &&
1917
+ streamRoute &&
1918
+ route?.kind === "page" &&
1919
+ fatalDiagnostics.every(
1920
+ (diagnostic) => diagnostic.code === "MR_UNSUPPORTED_AWAIT_INNER_COMPONENT",
1921
+ )
1922
+ ) {
1923
+ return undefined;
1924
+ }
1925
+
1926
+ throw new Error(
1927
+ fatalDiagnostics.map((diagnostic) => formatDiagnostic(file, diagnostic)).join("\n"),
1928
+ );
1929
+ }
1888
1930
 
1889
- if (fatalDiagnostics.length > 0) {
1890
1931
  if (
1891
1932
  serverOutput === "string" &&
1892
1933
  streamRoute &&
1893
1934
  route?.kind === "page" &&
1894
- fatalDiagnostics.every(
1895
- (diagnostic) => diagnostic.code === "MR_UNSUPPORTED_AWAIT_INNER_COMPONENT",
1896
- )
1935
+ source.includes("<Await") &&
1936
+ /\bLink\b/.test(source)
1897
1937
  ) {
1938
+ // Native Link renders pre-rendered children as HTML. Keep direct
1939
+ // Await renderers on the stream artifact so the string artifact
1940
+ // cannot drop deferred Link content before the boundary resolves.
1898
1941
  return undefined;
1899
1942
  }
1900
1943
 
1901
- throw new Error(
1902
- fatalDiagnostics.map((diagnostic) => formatDiagnostic(file, diagnostic)).join("\n"),
1903
- );
1904
- }
1944
+ return [
1945
+ serverOutput,
1946
+ {
1947
+ ...(options.prebundleServerComponents
1948
+ ? {
1949
+ bundleCode: await buildServerComponentBundleArtifactCode({
1950
+ clientRouteInferenceCache: options.clientRouteInferenceCache,
1951
+ code: output.code,
1952
+ filename: absoluteFile,
1953
+ root: options.projectRoot,
1954
+ serverOutput,
1955
+ vitePlugins: options.vitePlugins,
1956
+ }),
1957
+ }
1958
+ : {}),
1959
+ code: output.code,
1960
+ metadata: output.metadata,
1961
+ sourceHash: hashText(code),
1962
+ },
1963
+ ] as const;
1964
+ },
1965
+ );
1905
1966
 
1906
- if (
1907
- serverOutput === "string" &&
1908
- streamRoute &&
1909
- route?.kind === "page" &&
1910
- source.includes("<Await") &&
1911
- /\bLink\b/.test(source)
1912
- ) {
1913
- // Native Link renders pre-rendered children as HTML. Keep direct
1914
- // Await renderers on the stream artifact so the string artifact
1915
- // cannot drop deferred Link content before the boundary resolves.
1916
- return undefined;
1967
+ for (const entry of serverOutputArtifacts) {
1968
+ if (entry !== undefined) {
1969
+ artifact[entry[0]] = entry[1];
1917
1970
  }
1918
-
1919
- return [
1920
- serverOutput,
1921
- {
1922
- ...(options.prebundleServerComponents
1923
- ? {
1924
- bundleCode: await buildServerComponentBundleArtifactCode({
1925
- clientRouteInferenceCache: options.clientRouteInferenceCache,
1926
- code: output.code,
1927
- filename: absoluteFile,
1928
- root: options.projectRoot,
1929
- serverOutput,
1930
- vitePlugins: options.vitePlugins,
1931
- }),
1932
- }
1933
- : {}),
1934
- code: output.code,
1935
- metadata: output.metadata,
1936
- sourceHash: hashText(code),
1937
- },
1938
- ] as const;
1939
- },
1940
- );
1941
-
1942
- for (const entry of serverOutputArtifacts) {
1943
- if (entry !== undefined) {
1944
- artifact[entry[0]] = entry[1];
1945
1971
  }
1946
- }
1947
1972
 
1948
- return [file, artifact] as const;
1949
- });
1973
+ return [file, artifact] as const;
1974
+ },
1975
+ );
1950
1976
 
1951
1977
  for (const entry of artifactEntries) {
1952
1978
  if (entry !== undefined) {
@@ -1984,6 +2010,7 @@ async function buildServerComponentBundleArtifactCode(options: {
1984
2010
  async function transformServerRouteSource(options: {
1985
2011
  cache: ServerTransformCache;
1986
2012
  clientBoundaryImports: readonly string[];
2013
+ clientBoundaryFallbackImports?: readonly string[];
1987
2014
  code: string;
1988
2015
  filename: string;
1989
2016
  moduleContextCache: ClientRouteInferenceCache;
@@ -1991,6 +2018,7 @@ async function transformServerRouteSource(options: {
1991
2018
  }): Promise<ServerTransformOutput> {
1992
2019
  const cacheKey = stableCacheKey({
1993
2020
  clientBoundaryImports: options.clientBoundaryImports,
2021
+ clientBoundaryFallbackImports: options.clientBoundaryFallbackImports ?? [],
1994
2022
  codeHash: hashText(options.code),
1995
2023
  filename: resolve(options.filename),
1996
2024
  serverOutput: options.serverOutput,
@@ -2012,6 +2040,7 @@ async function transformServerRouteSource(options: {
2012
2040
  return transformCompilerModuleContext({
2013
2041
  code: options.code,
2014
2042
  clientBoundaryImports: options.clientBoundaryImports,
2043
+ clientBoundaryFallbackImports: options.clientBoundaryFallbackImports ?? [],
2015
2044
  dev: false,
2016
2045
  filename: options.filename,
2017
2046
  moduleContext,
@@ -2029,8 +2058,11 @@ function builtRouteSourceAnalysisSummary(options: {
2029
2058
  }): BuiltRouteSourceAnalysisSummary {
2030
2059
  return {
2031
2060
  authIncludesClaims: options.analysis.authIncludesClaims,
2032
- ...(options.analysis.cachePolicy === undefined ? {} : { cachePolicy: options.analysis.cachePolicy }),
2061
+ ...(options.analysis.cachePolicy === undefined
2062
+ ? {}
2063
+ : { cachePolicy: options.analysis.cachePolicy }),
2033
2064
  clientBoundaryImports: options.analysis.clientBoundaryImports,
2065
+ clientBoundaryFallbackImports: options.analysis.clientBoundaryFallbackImports,
2034
2066
  clientRoute: options.analysis.clientRoute,
2035
2067
  hasLoader: options.analysis.hasLoader,
2036
2068
  routeCode: options.analysis.routeCode,
@@ -2089,7 +2121,10 @@ function usesRuntimeCacheControl(code: string): boolean {
2089
2121
  return /\bcacheControl\s*\(/.test(code);
2090
2122
  }
2091
2123
 
2092
- function routeRequestArtifactBatchKey(file: string, kind: "loader" | "metadata" | "request"): string {
2124
+ function routeRequestArtifactBatchKey(
2125
+ file: string,
2126
+ kind: "loader" | "metadata" | "request",
2127
+ ): string {
2093
2128
  return `${kind}:${file}`;
2094
2129
  }
2095
2130
 
@@ -2129,15 +2164,15 @@ async function buildRequestModuleArtifactCode(options: {
2129
2164
  });
2130
2165
  }
2131
2166
 
2132
- return await bundleRouteLoaderModuleCode({
2133
- appDir: options.appDir,
2134
- bundleCache: options.bundleCache,
2135
- cacheDir: options.cacheDir,
2136
- code: stripRouteRequestOnlyExports(options.source, options.filename),
2137
- filename: options.filename,
2138
- importPolicy: options.importPolicy,
2139
- vitePlugins: options.vitePlugins,
2140
- });
2167
+ return await bundleRouteLoaderModuleCode({
2168
+ appDir: options.appDir,
2169
+ bundleCache: options.bundleCache,
2170
+ cacheDir: options.cacheDir,
2171
+ code: stripRouteRequestOnlyExports(options.source, options.filename),
2172
+ filename: options.filename,
2173
+ importPolicy: options.importPolicy,
2174
+ vitePlugins: options.vitePlugins,
2175
+ });
2141
2176
  }
2142
2177
 
2143
2178
  async function bundleRouteLoaderModuleCode(options: {
@@ -2214,24 +2249,29 @@ async function bundleRouteRequestModuleBatchCode(options: {
2214
2249
  label: "Request artifact",
2215
2250
  }),
2216
2251
  ],
2217
- root: options.importPolicy?.projectRoot ?? dirname(options.entries[0]?.filename ?? options.appDir),
2252
+ root:
2253
+ options.importPolicy?.projectRoot ?? dirname(options.entries[0]?.filename ?? options.appDir),
2218
2254
  vitePlugins: options.vitePlugins,
2219
2255
  });
2220
2256
 
2221
2257
  if (output.chunks.some((chunk) => !chunk.isEntry)) {
2222
- const fallbackEntries = await mapWithBuildConcurrency(options.entries, async (entry) => [
2223
- entry.key,
2224
- await bundleRouteRequestModuleCode({
2225
- appDir: options.appDir,
2226
- bundleCache: options.bundleCache,
2227
- cacheDir: options.cacheDir,
2228
- code: entry.code,
2229
- filename: entry.filename,
2230
- importPolicy: options.importPolicy,
2231
- label: entry.label,
2232
- vitePlugins: options.vitePlugins,
2233
- }),
2234
- ] as const);
2258
+ const fallbackEntries = await mapWithBuildConcurrency(
2259
+ options.entries,
2260
+ async (entry) =>
2261
+ [
2262
+ entry.key,
2263
+ await bundleRouteRequestModuleCode({
2264
+ appDir: options.appDir,
2265
+ bundleCache: options.bundleCache,
2266
+ cacheDir: options.cacheDir,
2267
+ code: entry.code,
2268
+ filename: entry.filename,
2269
+ importPolicy: options.importPolicy,
2270
+ label: entry.label,
2271
+ vitePlugins: options.vitePlugins,
2272
+ }),
2273
+ ] as const,
2274
+ );
2235
2275
  return new Map(fallbackEntries);
2236
2276
  }
2237
2277
 
@@ -2346,9 +2386,11 @@ function isMiddlewareFile(appDir: string, file: string): boolean {
2346
2386
  }
2347
2387
 
2348
2388
  function hasMetadataExport(code: string): boolean {
2349
- return /\bexport\s+const\s+metadata\s*=/.test(code) ||
2389
+ return (
2390
+ /\bexport\s+const\s+metadata\s*=/.test(code) ||
2350
2391
  /\bexport\s+(?:async\s+)?function\s+generateMetadata\b/.test(code) ||
2351
- /\bexport\s*\{[^}]*\bgenerateMetadata\b[^}]*\}/.test(code);
2392
+ /\bexport\s*\{[^}]*\bgenerateMetadata\b[^}]*\}/.test(code)
2393
+ );
2352
2394
  }
2353
2395
 
2354
2396
  async function readDeclaredProjectPackages(projectRoot: string): Promise<string[]> {
@@ -2432,11 +2474,13 @@ async function writeCloudflareRouteModules(options: {
2432
2474
 
2433
2475
  return analysis === undefined
2434
2476
  ? []
2435
- : [{
2436
- route,
2437
- routeFile,
2438
- routeId: routeIdForPath(route.path),
2439
- }];
2477
+ : [
2478
+ {
2479
+ route,
2480
+ routeFile,
2481
+ routeId: routeIdForPath(route.path),
2482
+ },
2483
+ ];
2440
2484
  }),
2441
2485
  );
2442
2486
 
@@ -2455,8 +2499,10 @@ async function writeCloudflareRouteModules(options: {
2455
2499
  cacheDir: options.cacheDir,
2456
2500
  define: options.define,
2457
2501
  routes: requiredRoutes
2458
- .filter(({ route, routeFile }) =>
2459
- route.kind === "page" && options.sourceAnalysis.byRouteFile.get(routeFile)?.hasLoader === true
2502
+ .filter(
2503
+ ({ route, routeFile }) =>
2504
+ route.kind === "page" &&
2505
+ options.sourceAnalysis.byRouteFile.get(routeFile)?.hasLoader === true,
2460
2506
  )
2461
2507
  .map(({ route, routeId }) => ({ filename: route.file, routeId })),
2462
2508
  root: options.projectRoot,
@@ -2504,114 +2550,120 @@ async function writeCloudflareRouteModules(options: {
2504
2550
  writeCloudflareBatchedRouteModuleChunks(options.cloudflareDir, stringShellComponentModules),
2505
2551
  ]);
2506
2552
 
2507
- const registryEntries = await mapWithBuildConcurrency(requiredRoutes, async ({ route, routeFile, routeId }) => {
2508
- const routeModuleFile = `routes/${routeId}.mjs`;
2509
- let routeModuleExports: string[];
2553
+ const registryEntries = await mapWithBuildConcurrency(
2554
+ requiredRoutes,
2555
+ async ({ route, routeFile, routeId }) => {
2556
+ const routeModuleFile = `routes/${routeId}.mjs`;
2557
+ let routeModuleExports: string[];
2510
2558
 
2511
- if (route.kind === "server" || route.kind === "metadata") {
2512
- try {
2513
- const serverRouteModule = serverRouteModules.entries.get(routeId);
2559
+ if (route.kind === "server" || route.kind === "metadata") {
2560
+ try {
2561
+ const serverRouteModule = serverRouteModules.entries.get(routeId);
2514
2562
 
2515
- if (serverRouteModule === undefined) {
2516
- throw new Error(`Missing bundled Cloudflare ${route.kind} route module.`);
2563
+ if (serverRouteModule === undefined) {
2564
+ throw new Error(`Missing bundled Cloudflare ${route.kind} route module.`);
2565
+ }
2566
+
2567
+ const serverRouteFile = serverRouteModule.fileName;
2568
+ const serverRouteImport = `./${serverRouteFile.split("/").pop() ?? serverRouteFile}`;
2569
+ routeModuleExports = [
2570
+ `export * from ${JSON.stringify(serverRouteImport)};`,
2571
+ ...(route.kind === "metadata"
2572
+ ? [`export { default } from ${JSON.stringify(serverRouteImport)};`]
2573
+ : []),
2574
+ ];
2575
+ } catch (error) {
2576
+ throw new Error(
2577
+ `Failed to build Cloudflare ${route.kind} route module for ${routeFile}: ${errorMessage(error)}`,
2578
+ );
2517
2579
  }
2518
2580
 
2519
- const serverRouteFile = serverRouteModule.fileName;
2520
- const serverRouteImport = `./${serverRouteFile.split("/").pop() ?? serverRouteFile}`;
2521
- routeModuleExports = [
2522
- `export * from ${JSON.stringify(serverRouteImport)};`,
2523
- ...(route.kind === "metadata"
2524
- ? [`export { default } from ${JSON.stringify(serverRouteImport)};`]
2525
- : []),
2526
- ];
2527
- } catch (error) {
2528
- throw new Error(
2529
- `Failed to build Cloudflare ${route.kind} route module for ${routeFile}: ${errorMessage(error)}`,
2581
+ await writeFile(
2582
+ join(options.cloudflareDir, routeModuleFile),
2583
+ `${routeModuleExports.join("\n")}\n`,
2530
2584
  );
2585
+ return `${JSON.stringify(routeFile)}: () => import(${JSON.stringify(`./${routeModuleFile}`)})`;
2531
2586
  }
2532
2587
 
2533
- await writeFile(join(options.cloudflareDir, routeModuleFile), `${routeModuleExports.join("\n")}\n`);
2534
- return `${JSON.stringify(routeFile)}: () => import(${JSON.stringify(`./${routeModuleFile}`)})`;
2535
- }
2536
-
2537
- const serverOutput =
2538
- options.serverModules[routeFile]?.analysis?.streamRoute === true ||
2539
- options.sourceAnalysis.byRouteFile.get(routeFile)?.streamRoute === true
2540
- ? "stream"
2541
- : "string";
2588
+ const serverOutput =
2589
+ options.serverModules[routeFile]?.analysis?.streamRoute === true ||
2590
+ options.sourceAnalysis.byRouteFile.get(routeFile)?.streamRoute === true
2591
+ ? "stream"
2592
+ : "string";
2542
2593
 
2543
- try {
2544
- const batchedComponent =
2545
- stringShellComponentRouteIds.has(routeId)
2594
+ try {
2595
+ const batchedComponent = stringShellComponentRouteIds.has(routeId)
2546
2596
  ? stringShellComponentModules.entries.get(routeId)
2547
2597
  : directComponentRouteIds.has(routeId)
2548
2598
  ? directComponentModules.entries.get(routeId)
2549
2599
  : undefined;
2550
- let componentFile = batchedComponent?.fileName;
2551
-
2552
- if (batchedComponent === undefined) {
2553
- const componentOutput =
2554
- serverOutput === "stream"
2555
- ? await buildCloudflareStreamRouteComponentModule({
2556
- cacheDir: options.cacheDir,
2557
- define: options.define,
2558
- filename: route.file,
2559
- projectRoot: options.projectRoot,
2560
- routesDir: options.routesDir,
2561
- serverModules: options.serverModules,
2562
- sourceAnalysis: options.sourceAnalysis,
2563
- vitePlugins: options.vitePlugins,
2564
- })
2565
- : await buildCloudflareStringRouteComponentModule({
2566
- cacheDir: options.cacheDir,
2567
- define: options.define,
2568
- filename: route.file,
2569
- projectRoot: options.projectRoot,
2570
- routesDir: options.routesDir,
2571
- serverModules: options.serverModules,
2572
- sourceAnalysis: options.sourceAnalysis,
2573
- vitePlugins: options.vitePlugins,
2574
- });
2600
+ let componentFile = batchedComponent?.fileName;
2601
+
2602
+ if (batchedComponent === undefined) {
2603
+ const componentOutput =
2604
+ serverOutput === "stream"
2605
+ ? await buildCloudflareStreamRouteComponentModule({
2606
+ cacheDir: options.cacheDir,
2607
+ define: options.define,
2608
+ filename: route.file,
2609
+ projectRoot: options.projectRoot,
2610
+ routesDir: options.routesDir,
2611
+ serverModules: options.serverModules,
2612
+ sourceAnalysis: options.sourceAnalysis,
2613
+ vitePlugins: options.vitePlugins,
2614
+ })
2615
+ : await buildCloudflareStringRouteComponentModule({
2616
+ cacheDir: options.cacheDir,
2617
+ define: options.define,
2618
+ filename: route.file,
2619
+ projectRoot: options.projectRoot,
2620
+ routesDir: options.routesDir,
2621
+ serverModules: options.serverModules,
2622
+ sourceAnalysis: options.sourceAnalysis,
2623
+ vitePlugins: options.vitePlugins,
2624
+ });
2625
+
2626
+ componentFile = `routes/${routeId}.${hashText(componentOutput).slice(0, 8)}.component.mjs`;
2627
+ await writeFile(join(options.cloudflareDir, componentFile), componentOutput);
2628
+ }
2575
2629
 
2576
- componentFile = `routes/${routeId}.${hashText(componentOutput).slice(0, 8)}.component.mjs`;
2577
- await writeFile(join(options.cloudflareDir, componentFile), componentOutput);
2578
- }
2630
+ if (componentFile === undefined) {
2631
+ throw new Error("Missing bundled Cloudflare component module.");
2632
+ }
2579
2633
 
2580
- if (componentFile === undefined) {
2581
- throw new Error("Missing bundled Cloudflare component module.");
2634
+ const componentImport = `./${componentFile.split("/").pop() ?? componentFile}`;
2635
+ routeModuleExports = [cloudflarePageRouteFacadeModuleSource(componentImport)];
2636
+ } catch (error) {
2637
+ throw new Error(
2638
+ `Failed to build Cloudflare route module for ${routeFile}: ${errorMessage(error)}`,
2639
+ );
2582
2640
  }
2583
2641
 
2584
- const componentImport = `./${componentFile.split("/").pop() ?? componentFile}`;
2585
- routeModuleExports = [
2586
- cloudflarePageRouteFacadeModuleSource(componentImport),
2587
- ];
2588
- } catch (error) {
2589
- throw new Error(
2590
- `Failed to build Cloudflare route module for ${routeFile}: ${errorMessage(error)}`,
2591
- );
2592
- }
2642
+ if (options.sourceAnalysis.byRouteFile.get(routeFile)?.hasLoader === true) {
2643
+ try {
2644
+ const loaderModule = loaderRouteModules.entries.get(routeId);
2593
2645
 
2594
- if (options.sourceAnalysis.byRouteFile.get(routeFile)?.hasLoader === true) {
2595
- try {
2596
- const loaderModule = loaderRouteModules.entries.get(routeId);
2646
+ if (loaderModule === undefined) {
2647
+ throw new Error("Missing bundled Cloudflare loader module.");
2648
+ }
2597
2649
 
2598
- if (loaderModule === undefined) {
2599
- throw new Error("Missing bundled Cloudflare loader module.");
2650
+ const loaderFile = loaderModule.fileName;
2651
+ const loaderImport = `./${loaderFile.split("/").pop() ?? loaderFile}`;
2652
+ routeModuleExports.push(`export { loader } from ${JSON.stringify(loaderImport)};`);
2653
+ } catch (error) {
2654
+ throw new Error(
2655
+ `Failed to build Cloudflare loader module for ${routeFile}: ${errorMessage(error)}`,
2656
+ );
2600
2657
  }
2601
-
2602
- const loaderFile = loaderModule.fileName;
2603
- const loaderImport = `./${loaderFile.split("/").pop() ?? loaderFile}`;
2604
- routeModuleExports.push(`export { loader } from ${JSON.stringify(loaderImport)};`);
2605
- } catch (error) {
2606
- throw new Error(
2607
- `Failed to build Cloudflare loader module for ${routeFile}: ${errorMessage(error)}`,
2608
- );
2609
2658
  }
2610
- }
2611
2659
 
2612
- await writeFile(join(options.cloudflareDir, routeModuleFile), `${routeModuleExports.join("\n")}\n`);
2613
- return `${JSON.stringify(routeFile)}: () => import(${JSON.stringify(`./${routeModuleFile}`)})`;
2614
- });
2660
+ await writeFile(
2661
+ join(options.cloudflareDir, routeModuleFile),
2662
+ `${routeModuleExports.join("\n")}\n`,
2663
+ );
2664
+ return `${JSON.stringify(routeFile)}: () => import(${JSON.stringify(`./${routeModuleFile}`)})`;
2665
+ },
2666
+ );
2615
2667
 
2616
2668
  const registrySource = [
2617
2669
  `export const routeModules = {`,
@@ -2893,7 +2945,9 @@ async function buildCloudflareStringRouteComponentModuleBatch(options: {
2893
2945
  contents: pageMetadataModule,
2894
2946
  resolveDir: dirname(route.filename),
2895
2947
  });
2896
- metadataImports.push(`import * as pageMetadataModule from ${JSON.stringify(pageMetadataId)};`);
2948
+ metadataImports.push(
2949
+ `import * as pageMetadataModule from ${JSON.stringify(pageMetadataId)};`,
2950
+ );
2897
2951
  }
2898
2952
 
2899
2953
  const shellMetadataImports = await Promise.all(
@@ -3084,7 +3138,11 @@ async function buildCloudflareServerComponentModule(options: {
3084
3138
  cacheDir: options.cacheDir,
3085
3139
  define: options.define,
3086
3140
  filename: options.filename,
3087
- hasMetadata: buildSourceAnalysisForFile(options.sourceAnalysis, options.projectRoot, options.filename)?.hasMetadata,
3141
+ hasMetadata: buildSourceAnalysisForFile(
3142
+ options.sourceAnalysis,
3143
+ options.projectRoot,
3144
+ options.filename,
3145
+ )?.hasMetadata,
3088
3146
  root: options.projectRoot,
3089
3147
  vitePlugins: options.vitePlugins,
3090
3148
  });
@@ -3098,9 +3156,8 @@ async function buildCloudflareServerComponentModule(options: {
3098
3156
  filename: `${options.filename}.mreact-cloudflare-component.js`,
3099
3157
  cacheDir: options.cacheDir,
3100
3158
  define: options.define,
3101
- modules: metadataModule === undefined
3102
- ? new Map()
3103
- : new Map([["mreact:metadata", metadataModule]]),
3159
+ modules:
3160
+ metadataModule === undefined ? new Map() : new Map([["mreact:metadata", metadataModule]]),
3104
3161
  plugins: [
3105
3162
  cloudflareServerSourceTransformPlugin({
3106
3163
  projectRoot: options.projectRoot,
@@ -3180,7 +3237,9 @@ async function buildCloudflareStringRouteComponentModule(options: {
3180
3237
  }),
3181
3238
  ),
3182
3239
  );
3183
- const shellImports = shellFiles.map((_, index) => `import * as shell${index} from "mreact:shell-${index}";`);
3240
+ const shellImports = shellFiles.map(
3241
+ (_, index) => `import * as shell${index} from "mreact:shell-${index}";`,
3242
+ );
3184
3243
  const shellDefinitions = shellFiles.map(
3185
3244
  (shell, index) =>
3186
3245
  `{ component: selectComponent(shell${index}, ${JSON.stringify(shell.file)}), id: ${JSON.stringify(shell.id)}, kind: ${JSON.stringify(shell.kind)}, module: shell${index} }`,
@@ -3287,7 +3346,9 @@ async function buildCloudflareStreamRouteComponentModule(options: {
3287
3346
  }),
3288
3347
  ),
3289
3348
  );
3290
- const shellImports = shellFiles.map((_, index) => `import * as shell${index} from "mreact:shell-${index}";`);
3349
+ const shellImports = shellFiles.map(
3350
+ (_, index) => `import * as shell${index} from "mreact:shell-${index}";`,
3351
+ );
3291
3352
  const shellDefinitions = shellFiles.map(
3292
3353
  (shell, index) =>
3293
3354
  `{ component: selectComponent(shell${index}, ${JSON.stringify(shell.file)}), id: ${JSON.stringify(shell.id)}, kind: ${JSON.stringify(shell.kind)}, module: shell${index} }`,
@@ -3687,7 +3748,11 @@ async function buildCloudflareComponentExportModule(options: {
3687
3748
  cacheDir: options.cacheDir,
3688
3749
  define: options.define,
3689
3750
  filename: options.filename,
3690
- hasMetadata: buildSourceAnalysisForFile(options.sourceAnalysis, options.projectRoot, options.filename)?.hasMetadata,
3751
+ hasMetadata: buildSourceAnalysisForFile(
3752
+ options.sourceAnalysis,
3753
+ options.projectRoot,
3754
+ options.filename,
3755
+ )?.hasMetadata,
3691
3756
  root: options.projectRoot,
3692
3757
  vitePlugins: options.vitePlugins,
3693
3758
  });
@@ -3706,9 +3771,8 @@ export const slots = routeModule.slots;`;
3706
3771
  cacheDir: options.cacheDir,
3707
3772
  define: options.define,
3708
3773
  filename: `${options.filename}.mreact-cloudflare-${options.serverOutput}-component.js`,
3709
- modules: metadataModule === undefined
3710
- ? new Map()
3711
- : new Map([["mreact:metadata", metadataModule]]),
3774
+ modules:
3775
+ metadataModule === undefined ? new Map() : new Map([["mreact:metadata", metadataModule]]),
3712
3776
  plugins: [
3713
3777
  cloudflareServerSourceTransformPlugin({
3714
3778
  projectRoot: options.projectRoot,
@@ -4093,6 +4157,7 @@ async function transformCloudflareServerSource(options: {
4093
4157
  const output = transformCompilerModuleContext({
4094
4158
  code: options.source,
4095
4159
  clientBoundaryImports: clientInference.clientBoundaryImports,
4160
+ clientBoundaryFallbackImports: clientInference.clientBoundaryFallbackImports,
4096
4161
  dev: false,
4097
4162
  filename: options.filename,
4098
4163
  moduleContext,
@@ -4106,7 +4171,9 @@ async function transformCloudflareServerSource(options: {
4106
4171
 
4107
4172
  if (fatalDiagnostics.length > 0) {
4108
4173
  throw new Error(
4109
- fatalDiagnostics.map((diagnostic) => formatDiagnostic(options.filename, diagnostic)).join("\n"),
4174
+ fatalDiagnostics
4175
+ .map((diagnostic) => formatDiagnostic(options.filename, diagnostic))
4176
+ .join("\n"),
4110
4177
  );
4111
4178
  }
4112
4179
 
@@ -4147,9 +4214,18 @@ function cloudflareWorkspaceRuntimePlugin(): RouterCompatPlugin {
4147
4214
  "@reckona/mreact-compat/event-priority",
4148
4215
  packageFile("react-compat", "@reckona/mreact-compat", "event-priority"),
4149
4216
  ],
4150
- ["@reckona/mreact-compat/flight", packageFile("react-compat", "@reckona/mreact-compat", "flight")],
4151
- ["@reckona/mreact-compat/hooks", packageFile("react-compat", "@reckona/mreact-compat", "hooks-entry")],
4152
- ["@reckona/mreact-compat/internal", packageFile("react-compat", "@reckona/mreact-compat", "internal")],
4217
+ [
4218
+ "@reckona/mreact-compat/flight",
4219
+ packageFile("react-compat", "@reckona/mreact-compat", "flight"),
4220
+ ],
4221
+ [
4222
+ "@reckona/mreact-compat/hooks",
4223
+ packageFile("react-compat", "@reckona/mreact-compat", "hooks-entry"),
4224
+ ],
4225
+ [
4226
+ "@reckona/mreact-compat/internal",
4227
+ packageFile("react-compat", "@reckona/mreact-compat", "internal"),
4228
+ ],
4153
4229
  [
4154
4230
  "@reckona/mreact-compat/jsx-dev-runtime",
4155
4231
  packageFile("react-compat", "@reckona/mreact-compat", "jsx-dev-runtime"),
@@ -4158,9 +4234,15 @@ function cloudflareWorkspaceRuntimePlugin(): RouterCompatPlugin {
4158
4234
  "@reckona/mreact-compat/jsx-runtime",
4159
4235
  packageFile("react-compat", "@reckona/mreact-compat", "jsx-runtime"),
4160
4236
  ],
4161
- ["@reckona/mreact-compat/scheduler", packageFile("react-compat", "@reckona/mreact-compat", "scheduler")],
4237
+ [
4238
+ "@reckona/mreact-compat/scheduler",
4239
+ packageFile("react-compat", "@reckona/mreact-compat", "scheduler"),
4240
+ ],
4162
4241
  ["@reckona/mreact-query", packageFile("query", "@reckona/mreact-query", "index")],
4163
- ["@reckona/mreact-reactive-core", packageFile("reactive-core", "@reckona/mreact-reactive-core", "index")],
4242
+ [
4243
+ "@reckona/mreact-reactive-core",
4244
+ packageFile("reactive-core", "@reckona/mreact-reactive-core", "index"),
4245
+ ],
4164
4246
  [
4165
4247
  "@reckona/mreact-router/adapters/cloudflare",
4166
4248
  packageFile("router", "@reckona/mreact-router", "adapters/cloudflare"),
@@ -4179,10 +4261,13 @@ function cloudflareWorkspaceRuntimePlugin(): RouterCompatPlugin {
4179
4261
  return {
4180
4262
  name: "mreact-cloudflare-workspace-runtime",
4181
4263
  setup(buildApi) {
4182
- buildApi.onResolve({ filter: /^@reckona\/mreact-router\/(?:internal\/)?native-escape$/ }, () => ({
4183
- namespace: "mreact-cloudflare-native-escape",
4184
- path: "native-escape",
4185
- }));
4264
+ buildApi.onResolve(
4265
+ { filter: /^@reckona\/mreact-router\/(?:internal\/)?native-escape$/ },
4266
+ () => ({
4267
+ namespace: "mreact-cloudflare-native-escape",
4268
+ path: "native-escape",
4269
+ }),
4270
+ );
4186
4271
  buildApi.onResolve({ filter: /^@reckona\/mreact-router$/ }, () => ({
4187
4272
  namespace: "mreact-cloudflare-router-index",
4188
4273
  path: "index",
@@ -4192,8 +4277,10 @@ function cloudflareWorkspaceRuntimePlugin(): RouterCompatPlugin {
4192
4277
 
4193
4278
  return path === undefined ? undefined : { path };
4194
4279
  });
4195
- buildApi.onLoad({ filter: /^native-escape$/, namespace: "mreact-cloudflare-native-escape" }, () => ({
4196
- contents: `function escapeHtml(value) {
4280
+ buildApi.onLoad(
4281
+ { filter: /^native-escape$/, namespace: "mreact-cloudflare-native-escape" },
4282
+ () => ({
4283
+ contents: `function escapeHtml(value) {
4197
4284
  return String(value ?? "").replace(/[&<>"']/g, (char) =>
4198
4285
  char === "&" ? "&amp;" : char === "<" ? "&lt;" : char === ">" ? "&gt;" : char === '"' ? "&quot;" : "&#39;"
4199
4286
  );
@@ -4201,8 +4288,9 @@ function cloudflareWorkspaceRuntimePlugin(): RouterCompatPlugin {
4201
4288
  export function escapeHtmlBatch(values) {
4202
4289
  return values.map(escapeHtml);
4203
4290
  }`,
4204
- loader: "js",
4205
- }));
4291
+ loader: "js",
4292
+ }),
4293
+ );
4206
4294
  buildApi.onLoad({ filter: /^index$/, namespace: "mreact-cloudflare-router-index" }, () => ({
4207
4295
  contents: `export { cacheControl, revalidatePath } from ${JSON.stringify(routerCachePath)};
4208
4296
  export { deleteCookie, parseCookieHeader, serializeCookie, setCookie } from ${JSON.stringify(routerCookiesPath)};
@@ -4216,12 +4304,15 @@ export { getServerRuntimeState } from ${JSON.stringify(routerRuntimeStatePath)};
4216
4304
  loader: "js",
4217
4305
  resolveDir: dirname(routerNavigationPath),
4218
4306
  }));
4219
- buildApi.onLoad({ filter: /(?:^|[/\\])packages[/\\]server[/\\](?:src|dist)[/\\]native-flight\.[jt]s$/ }, () => ({
4220
- contents: `export function getNativeFlight() {
4307
+ buildApi.onLoad(
4308
+ { filter: /(?:^|[/\\])packages[/\\]server[/\\](?:src|dist)[/\\]native-flight\.[jt]s$/ },
4309
+ () => ({
4310
+ contents: `export function getNativeFlight() {
4221
4311
  return undefined;
4222
4312
  }`,
4223
- loader: "js",
4224
- }));
4313
+ loader: "js",
4314
+ }),
4315
+ );
4225
4316
  },
4226
4317
  };
4227
4318
  }
@@ -4423,18 +4514,23 @@ async function writeClientRouteBundles(options: {
4423
4514
  });
4424
4515
  } catch (error) {
4425
4516
  const routeContexts = clientEntries
4426
- .map((entry) => `Failed to build client bundle for ${entry.route.path} (${entry.route.file}).`)
4517
+ .map(
4518
+ (entry) => `Failed to build client bundle for ${entry.route.path} (${entry.route.file}).`,
4519
+ )
4427
4520
  .join("\n");
4428
4521
 
4429
- throw new Error(`${routeContexts}\nFailed to build client route bundles.\n${errorMessage(error)}`, {
4430
- cause: error,
4431
- });
4522
+ throw new Error(
4523
+ `${routeContexts}\nFailed to build client route bundles.\n${errorMessage(error)}`,
4524
+ {
4525
+ cause: error,
4526
+ },
4527
+ );
4432
4528
  }
4433
4529
 
4434
4530
  const routeOutputs = new Map(output.routes.map((route) => [route.routePath, route]));
4435
4531
  const mapAssets = new Map(
4436
4532
  output.chunks.flatMap((chunk) =>
4437
- chunk.map === undefined ? [] : [[`${chunk.fileName}.map`, chunk.map] as const]
4533
+ chunk.map === undefined ? [] : [[`${chunk.fileName}.map`, chunk.map] as const],
4438
4534
  ),
4439
4535
  );
4440
4536
 
@@ -4459,9 +4555,7 @@ async function writeClientRouteBundles(options: {
4459
4555
 
4460
4556
  for (const asset of output.assets ?? []) {
4461
4557
  const source =
4462
- typeof asset.source === "string"
4463
- ? asset.source
4464
- : Buffer.from(asset.source).toString("utf8");
4558
+ typeof asset.source === "string" ? asset.source : Buffer.from(asset.source).toString("utf8");
4465
4559
 
4466
4560
  await mkdir(dirname(join(options.clientDir, asset.fileName)), { recursive: true });
4467
4561
  await writeFile(join(options.clientDir, asset.fileName), source);
@@ -4538,12 +4632,16 @@ async function writeRouteCssAssetBatches(options: {
4538
4632
  appDir: options.appDir,
4539
4633
  pageFile: route.file,
4540
4634
  projectRoot: options.projectRoot,
4541
- readSource: (file) => buildSourceAnalysisForFile(options.sourceAnalysis, options.projectRoot, file)?.source,
4635
+ readSource: (file) =>
4636
+ buildSourceAnalysisForFile(options.sourceAnalysis, options.projectRoot, file)?.source,
4542
4637
  });
4543
4638
 
4544
4639
  return { cssFiles, route };
4545
4640
  });
4546
- const groups = new Map<string, { cssFiles: string[]; routeIds: string[]; routeFiles: string[] }>();
4641
+ const groups = new Map<
4642
+ string,
4643
+ { cssFiles: string[]; routeIds: string[]; routeFiles: string[] }
4644
+ >();
4547
4645
 
4548
4646
  for (const { cssFiles, route } of cssInputs) {
4549
4647
  if (cssFiles.length === 0) {
@@ -4557,18 +4655,22 @@ async function writeRouteCssAssetBatches(options: {
4557
4655
  groups.set(key, group);
4558
4656
  }
4559
4657
 
4560
- const writtenGroups = await mapWithBuildConcurrency([...groups.entries()], async ([key, group]) => [
4561
- key,
4562
- await writeRouteCssAssetsForFiles({
4563
- cacheDir: options.cacheDir,
4564
- clientDir: options.clientDir,
4565
- cssFiles: group.cssFiles,
4566
- pageFile: group.routeFiles[0] ?? options.appDir,
4567
- projectRoot: options.projectRoot,
4568
- routeIds: group.routeIds,
4569
- vitePlugins: options.vitePlugins,
4570
- }),
4571
- ] as const);
4658
+ const writtenGroups = await mapWithBuildConcurrency(
4659
+ [...groups.entries()],
4660
+ async ([key, group]) =>
4661
+ [
4662
+ key,
4663
+ await writeRouteCssAssetsForFiles({
4664
+ cacheDir: options.cacheDir,
4665
+ clientDir: options.clientDir,
4666
+ cssFiles: group.cssFiles,
4667
+ pageFile: group.routeFiles[0] ?? options.appDir,
4668
+ projectRoot: options.projectRoot,
4669
+ routeIds: group.routeIds,
4670
+ vitePlugins: options.vitePlugins,
4671
+ }),
4672
+ ] as const,
4673
+ );
4572
4674
  const cssByGroup = new Map(writtenGroups);
4573
4675
  const cssByRoute = new Map<string, string[]>();
4574
4676
 
@@ -4612,15 +4714,14 @@ async function writeRouteCssAssetsForFiles(options: {
4612
4714
  });
4613
4715
  const cssAssets = (output.assets ?? []).filter((asset) => asset.fileName.endsWith(".css"));
4614
4716
  const written: string[] = [];
4615
- const routeStem = options.routeIds.length === 1
4616
- ? (options.routeIds[0] ?? "index")
4617
- : `shared.${hashText(cssFiles.join("\0")).slice(0, 8)}`;
4717
+ const routeStem =
4718
+ options.routeIds.length === 1
4719
+ ? (options.routeIds[0] ?? "index")
4720
+ : `shared.${hashText(cssFiles.join("\0")).slice(0, 8)}`;
4618
4721
 
4619
4722
  for (const [index, asset] of cssAssets.entries()) {
4620
4723
  const source =
4621
- typeof asset.source === "string"
4622
- ? asset.source
4623
- : Buffer.from(asset.source).toString("utf8");
4724
+ typeof asset.source === "string" ? asset.source : Buffer.from(asset.source).toString("utf8");
4624
4725
  const hash = createHash("sha256").update(source).digest("hex").slice(0, 8);
4625
4726
  const cssFile = `assets/routes/${routeStem}${cssAssets.length === 1 ? "" : `.${index}`}.${hash}.css`;
4626
4727
 
@@ -4798,7 +4899,9 @@ function isBarePackageImport(specifier: string): boolean {
4798
4899
 
4799
4900
  async function assertAwsLambdaRuntimeDependencies(outDir: string): Promise<void> {
4800
4901
  try {
4801
- const info = await stat(join(outDir, "node_modules", "@reckona", "mreact-router", "package.json"));
4902
+ const info = await stat(
4903
+ join(outDir, "node_modules", "@reckona", "mreact-router", "package.json"),
4904
+ );
4802
4905
  if (info.isFile()) {
4803
4906
  return;
4804
4907
  }
@@ -4891,7 +4994,9 @@ function assertPackageOutputDoesNotReplaceBuildOutput(options: {
4891
4994
  outDir: string;
4892
4995
  }): void {
4893
4996
  if (resolve(options.fromDir) === resolve(options.outDir)) {
4894
- throw new Error("Package output directory must be different from the mreact build output directory.");
4997
+ throw new Error(
4998
+ "Package output directory must be different from the mreact build output directory.",
4999
+ );
4895
5000
  }
4896
5001
  }
4897
5002
 
@@ -4955,7 +5060,9 @@ async function collectArtifactFiles(
4955
5060
  }
4956
5061
  }
4957
5062
 
4958
- return files.sort((left, right) => right.bytes - left.bytes || left.path.localeCompare(right.path));
5063
+ return files.sort(
5064
+ (left, right) => right.bytes - left.bytes || left.path.localeCompare(right.path),
5065
+ );
4959
5066
  }
4960
5067
 
4961
5068
  function awsLambdaHandlerSource(outDirRelativeToHandler: string): string {
@@ -5000,6 +5107,7 @@ async function validateProductionRoutes(options: {
5000
5107
  cache: options.serverTransformCache,
5001
5108
  code: analysis.routeCode,
5002
5109
  clientBoundaryImports: analysis.clientBoundaryImports,
5110
+ clientBoundaryFallbackImports: analysis.clientBoundaryFallbackImports,
5003
5111
  filename: route.file,
5004
5112
  moduleContextCache: options.clientRouteInferenceCache,
5005
5113
  serverOutput: analysis.streamRoute ? "stream" : "string",
@@ -5039,10 +5147,10 @@ async function collectBuildFiles(
5039
5147
 
5040
5148
  return [{ file, relativeFile }];
5041
5149
  });
5042
- const fileContents = await mapWithBuildConcurrency(sourceFiles, async ({ file, relativeFile }) => [
5043
- relativeFile,
5044
- await readFile(file, "utf8"),
5045
- ] as const);
5150
+ const fileContents = await mapWithBuildConcurrency(
5151
+ sourceFiles,
5152
+ async ({ file, relativeFile }) => [relativeFile, await readFile(file, "utf8")] as const,
5153
+ );
5046
5154
 
5047
5155
  for (const [relativeFile, source] of fileContents) {
5048
5156
  files[relativeFile] = source;