@reckona/mreact-router 0.0.131 → 0.0.134

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.
Files changed (48) hide show
  1. package/dist/adapters/cloudflare.js +1 -1
  2. package/dist/adapters/cloudflare.js.map +1 -1
  3. package/dist/adapters/static.js +5 -1
  4. package/dist/adapters/static.js.map +1 -1
  5. package/dist/build.d.ts +1 -0
  6. package/dist/build.d.ts.map +1 -1
  7. package/dist/build.js +136 -41
  8. package/dist/build.js.map +1 -1
  9. package/dist/client.d.ts.map +1 -1
  10. package/dist/client.js +188 -6
  11. package/dist/client.js.map +1 -1
  12. package/dist/http.d.ts +1 -0
  13. package/dist/http.d.ts.map +1 -1
  14. package/dist/http.js +7 -1
  15. package/dist/http.js.map +1 -1
  16. package/dist/module-runner.d.ts +4 -1
  17. package/dist/module-runner.d.ts.map +1 -1
  18. package/dist/module-runner.js +3 -2
  19. package/dist/module-runner.js.map +1 -1
  20. package/dist/render.d.ts +6 -1
  21. package/dist/render.d.ts.map +1 -1
  22. package/dist/render.js +84 -46
  23. package/dist/render.js.map +1 -1
  24. package/dist/routes.js +1 -1
  25. package/dist/routes.js.map +1 -1
  26. package/dist/serve.d.ts.map +1 -1
  27. package/dist/serve.js +110 -17
  28. package/dist/serve.js.map +1 -1
  29. package/dist/vite-plugin-cache-key.d.ts +2 -1
  30. package/dist/vite-plugin-cache-key.d.ts.map +1 -1
  31. package/dist/vite-plugin-cache-key.js +9 -0
  32. package/dist/vite-plugin-cache-key.js.map +1 -1
  33. package/dist/vite.d.ts +2 -1
  34. package/dist/vite.d.ts.map +1 -1
  35. package/dist/vite.js +2 -0
  36. package/dist/vite.js.map +1 -1
  37. package/package.json +11 -11
  38. package/src/adapters/cloudflare.ts +1 -1
  39. package/src/adapters/static.ts +7 -1
  40. package/src/build.ts +201 -53
  41. package/src/client.ts +329 -8
  42. package/src/http.ts +9 -1
  43. package/src/module-runner.ts +8 -2
  44. package/src/render.ts +117 -21
  45. package/src/routes.ts +1 -1
  46. package/src/serve.ts +167 -18
  47. package/src/vite-plugin-cache-key.ts +12 -1
  48. package/src/vite.ts +4 -0
package/src/build.ts CHANGED
@@ -1,6 +1,16 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import type { Dirent } from "node:fs";
3
- import { copyFile, cp, mkdir, readdir, readFile, rm, stat, writeFile } from "node:fs/promises";
3
+ import {
4
+ copyFile,
5
+ cp,
6
+ lstat,
7
+ mkdir,
8
+ readdir,
9
+ readFile,
10
+ rm,
11
+ stat,
12
+ writeFile,
13
+ } from "node:fs/promises";
4
14
  import { builtinModules } from "node:module";
5
15
  import { availableParallelism } from "node:os";
6
16
  import { dirname, join, relative, resolve, sep } from "node:path";
@@ -77,7 +87,7 @@ import { collectRouteCssFilesFromSources, collectSpecialBoundaryFiles } from "./
77
87
  import { existingRouteShellCandidates } from "./route-shells.js";
78
88
  import { sourceModuleCandidates } from "./source-modules.js";
79
89
  import { collectBuildInferredServerActions } from "./server-action-inference.js";
80
- import { vitePluginsCacheKey } from "./vite-plugin-cache-key.js";
90
+ import { viteDefineCacheKey, vitePluginsCacheKey } from "./vite-plugin-cache-key.js";
81
91
  import { workspacePackageFile } from "./workspace-packages.js";
82
92
  import type { PluginOption, UserConfig } from "vite";
83
93
 
@@ -380,10 +390,11 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
380
390
  }),
381
391
  ),
382
392
  timingSink === undefined
383
- ? buildServerModuleArtifacts({
384
- bundleCache: new Map(),
385
- clientRouteInferenceCache: serverClientRouteInferenceCache,
386
- files,
393
+ ? buildServerModuleArtifacts({
394
+ bundleCache: new Map(),
395
+ clientRouteInferenceCache: serverClientRouteInferenceCache,
396
+ define: viteDefine,
397
+ files,
387
398
  prebundleServerComponents: buildTargets.includes("node") || shouldBuildAwsLambda,
388
399
  project,
389
400
  projectRoot: project.projectRoot,
@@ -396,6 +407,7 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
396
407
  buildServerModuleArtifacts({
397
408
  bundleCache: new Map(),
398
409
  clientRouteInferenceCache: serverClientRouteInferenceCache,
410
+ define: viteDefine,
399
411
  files,
400
412
  prebundleServerComponents: buildTargets.includes("node") || shouldBuildAwsLambda,
401
413
  project,
@@ -501,6 +513,7 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
501
513
  appDir: project.routesDir,
502
514
  assetBaseUrl: project.assetBaseUrl,
503
515
  clientRoutes: clientManifestRoutes,
516
+ define: viteDefine,
504
517
  project,
505
518
  routes,
506
519
  serverModules,
@@ -512,6 +525,7 @@ export async function buildApp(options: BuildAppOptions): Promise<BuildAppResult
512
525
  appDir: project.routesDir,
513
526
  assetBaseUrl: project.assetBaseUrl,
514
527
  clientRoutes: clientManifestRoutes,
528
+ define: viteDefine,
515
529
  project,
516
530
  routes,
517
531
  serverModules,
@@ -1210,12 +1224,14 @@ async function collectRuntimeOptionalPackages(options: {
1210
1224
  const queue: Array<{ packageName: string; optional: boolean; startDir: string }> = [
1211
1225
  { packageName: options.packageName, optional: false, startDir: options.projectRoot },
1212
1226
  ];
1227
+ let stoppedAtManifestReadCap = false;
1228
+
1229
+ for (let index = 0; index < queue.length; index += 1) {
1230
+ if (seenPackageJson.size >= maxRuntimePackageManifestReads) {
1231
+ stoppedAtManifestReadCap = true;
1232
+ break;
1233
+ }
1213
1234
 
1214
- for (
1215
- let index = 0;
1216
- index < queue.length && seenPackageJson.size < maxRuntimePackageManifestReads;
1217
- index += 1
1218
- ) {
1219
1235
  const item = queue[index];
1220
1236
  if (item === undefined || !isValidRuntimePackageName(item.packageName)) {
1221
1237
  continue;
@@ -1250,6 +1266,17 @@ async function collectRuntimeOptionalPackages(options: {
1250
1266
  }
1251
1267
  }
1252
1268
 
1269
+ if (stoppedAtManifestReadCap) {
1270
+ console.warn(
1271
+ [
1272
+ "MR_RUNTIME_PACKAGE_MANIFEST_SCAN_LIMIT:",
1273
+ `stopped scanning optional runtime package manifests after ${maxRuntimePackageManifestReads} files`,
1274
+ `while collecting transitive optional dependencies for ${options.packageName}.`,
1275
+ "Generated import-policy.json may omit deeper optional runtime packages.",
1276
+ ].join(" "),
1277
+ );
1278
+ }
1279
+
1253
1280
  return [...optionalPackages].sort();
1254
1281
  }
1255
1282
 
@@ -1455,36 +1482,36 @@ function moduleIdForBuildFile(file: string, relativeRoutesDir: string): string {
1455
1482
  }
1456
1483
 
1457
1484
  async function copyPublicAssets(publicDir: string, outDir: string): Promise<void> {
1458
- try {
1459
- await cp(publicDir, outDir, { force: true, recursive: true });
1460
- } catch (error) {
1461
- if (isNodeError(error) && error.code === "ENOENT") {
1462
- return;
1463
- }
1464
-
1465
- throw error;
1485
+ if (!(await isPublicAssetDirectory(publicDir))) {
1486
+ return;
1466
1487
  }
1488
+
1489
+ await copyPublicAssetsInner(publicDir, "", outDir);
1467
1490
  }
1468
1491
 
1469
1492
  async function collectPublicAssetPaths(publicDir: string): Promise<string[]> {
1493
+ if (!(await isPublicAssetDirectory(publicDir))) {
1494
+ return [];
1495
+ }
1496
+
1497
+ const paths: string[] = [];
1498
+ await collectPublicAssetPathsInner(publicDir, "", paths);
1499
+
1500
+ return paths.sort();
1501
+ }
1502
+
1503
+ async function isPublicAssetDirectory(publicDir: string): Promise<boolean> {
1470
1504
  try {
1471
- const info = await stat(publicDir);
1505
+ const info = await lstat(publicDir);
1472
1506
 
1473
- if (!info.isDirectory()) {
1474
- return [];
1475
- }
1507
+ return info.isDirectory();
1476
1508
  } catch (error) {
1477
1509
  if (isNodeError(error) && error.code === "ENOENT") {
1478
- return [];
1510
+ return false;
1479
1511
  }
1480
1512
 
1481
1513
  throw error;
1482
1514
  }
1483
-
1484
- const paths: string[] = [];
1485
- await collectPublicAssetPathsInner(publicDir, "", paths);
1486
-
1487
- return paths.sort();
1488
1515
  }
1489
1516
 
1490
1517
  async function copyAppFileConventionAssets(appDir: string, outDir: string): Promise<string[]> {
@@ -1529,6 +1556,11 @@ async function collectPublicAssetPathsInner(
1529
1556
 
1530
1557
  for (const entry of entries) {
1531
1558
  const relativePath = relativeDir === "" ? entry.name : `${relativeDir}/${entry.name}`;
1559
+ const publicPath = `/${relativePath}`;
1560
+
1561
+ if (isReservedPublicAssetPath(publicPath)) {
1562
+ continue;
1563
+ }
1532
1564
 
1533
1565
  if (entry.isDirectory()) {
1534
1566
  await collectPublicAssetPathsInner(publicDir, relativePath, paths);
@@ -1536,11 +1568,42 @@ async function collectPublicAssetPathsInner(
1536
1568
  }
1537
1569
 
1538
1570
  if (entry.isFile()) {
1539
- paths.push(`/${relativePath}`);
1571
+ paths.push(publicPath);
1540
1572
  }
1541
1573
  }
1542
1574
  }
1543
1575
 
1576
+ async function copyPublicAssetsInner(
1577
+ publicDir: string,
1578
+ relativeDir: string,
1579
+ outDir: string,
1580
+ ): Promise<void> {
1581
+ const entries = await readdir(join(publicDir, relativeDir), { withFileTypes: true });
1582
+
1583
+ for (const entry of entries) {
1584
+ const relativePath = relativeDir === "" ? entry.name : `${relativeDir}/${entry.name}`;
1585
+
1586
+ if (isReservedPublicAssetPath(`/${relativePath}`)) {
1587
+ continue;
1588
+ }
1589
+
1590
+ if (entry.isDirectory()) {
1591
+ await copyPublicAssetsInner(publicDir, relativePath, outDir);
1592
+ continue;
1593
+ }
1594
+
1595
+ if (entry.isFile()) {
1596
+ const destination = join(outDir, relativePath);
1597
+ await mkdir(dirname(destination), { recursive: true });
1598
+ await copyFile(join(publicDir, relativePath), destination);
1599
+ }
1600
+ }
1601
+ }
1602
+
1603
+ function isReservedPublicAssetPath(pathname: string): boolean {
1604
+ return pathname === "/_mreact" || pathname.startsWith("/_mreact/");
1605
+ }
1606
+
1544
1607
  function isNodeError(error: unknown): error is NodeJS.ErrnoException {
1545
1608
  return error instanceof Error && "code" in error;
1546
1609
  }
@@ -1549,6 +1612,7 @@ async function prerenderStaticRoutes(options: {
1549
1612
  appDir: string;
1550
1613
  assetBaseUrl?: string | undefined;
1551
1614
  clientRoutes: readonly ClientRouteManifestEntry[];
1615
+ define?: UserConfig["define"] | undefined;
1552
1616
  project: ResolvedAppRouterProject;
1553
1617
  routes: readonly AppRoute[];
1554
1618
  serverModules: Record<string, BuiltServerModuleArtifact>;
@@ -1560,6 +1624,11 @@ async function prerenderStaticRoutes(options: {
1560
1624
  route.client && route.script !== undefined ? [[route.path, route.script]] : [],
1561
1625
  ),
1562
1626
  );
1627
+ const clientStyles = new Map(
1628
+ options.clientRoutes.flatMap((route) =>
1629
+ route.css !== undefined && route.css.length > 0 ? [[route.path, route.css]] : [],
1630
+ ),
1631
+ );
1563
1632
  const prerendered: Record<string, BuiltPrerenderedRoute> = {};
1564
1633
  const importPolicy = {
1565
1634
  allowedPackages: await readDeclaredProjectPackages(options.project.projectRoot),
@@ -1589,6 +1658,8 @@ async function prerenderStaticRoutes(options: {
1589
1658
  appDir: options.appDir,
1590
1659
  assetBaseUrl: options.assetBaseUrl,
1591
1660
  clientScripts,
1661
+ clientStyles,
1662
+ define: options.define,
1592
1663
  importPolicy,
1593
1664
  request: new Request(`http://mreact.local${pathname}`),
1594
1665
  serverModules: serverModuleMap,
@@ -1680,6 +1751,7 @@ async function buildServerModuleArtifacts(options: {
1680
1751
  bundleCache: Map<string, Promise<RouterBundleOutput>>;
1681
1752
  cacheDir?: string | undefined;
1682
1753
  clientRouteInferenceCache: ClientRouteInferenceCache;
1754
+ define?: UserConfig["define"] | undefined;
1683
1755
  files: Record<string, string>;
1684
1756
  prebundleServerComponents: boolean;
1685
1757
  project: ResolvedAppRouterProject;
@@ -1770,6 +1842,7 @@ async function buildServerModuleArtifacts(options: {
1770
1842
  cacheDir: options.cacheDir,
1771
1843
  entries: requestBatchEntries,
1772
1844
  importPolicy: requestModuleImportPolicy,
1845
+ define: options.define,
1773
1846
  vitePlugins: options.vitePlugins,
1774
1847
  })
1775
1848
  : new Map<string, string>();
@@ -1797,6 +1870,7 @@ async function buildServerModuleArtifacts(options: {
1797
1870
  code: stripRouteLoaderOnlyExports(source, absoluteFile),
1798
1871
  filename: absoluteFile,
1799
1872
  importPolicy: requestModuleImportPolicy,
1873
+ define: options.define,
1800
1874
  vitePlugins: options.vitePlugins,
1801
1875
  }));
1802
1876
  artifact.loader = {
@@ -1816,6 +1890,7 @@ async function buildServerModuleArtifacts(options: {
1816
1890
  filename: absoluteFile,
1817
1891
  importPolicy: requestModuleImportPolicy,
1818
1892
  label: "Metadata",
1893
+ define: options.define,
1819
1894
  vitePlugins: options.vitePlugins,
1820
1895
  }));
1821
1896
  artifact.routeMetadata = {
@@ -1837,6 +1912,7 @@ async function buildServerModuleArtifacts(options: {
1837
1912
  cacheDir: options.cacheDir,
1838
1913
  filename: absoluteFile,
1839
1914
  importPolicy: requestModuleImportPolicy,
1915
+ define: options.define,
1840
1916
  routeKind: route?.kind,
1841
1917
  source,
1842
1918
  vitePlugins: options.vitePlugins,
@@ -1952,6 +2028,7 @@ async function buildServerModuleArtifacts(options: {
1952
2028
  clientRouteInferenceCache: options.clientRouteInferenceCache,
1953
2029
  code: output.code,
1954
2030
  filename: absoluteFile,
2031
+ define: options.define,
1955
2032
  root: options.projectRoot,
1956
2033
  serverOutput,
1957
2034
  vitePlugins: options.vitePlugins,
@@ -1988,6 +2065,7 @@ async function buildServerModuleArtifacts(options: {
1988
2065
  async function buildServerComponentBundleArtifactCode(options: {
1989
2066
  clientRouteInferenceCache: ClientRouteInferenceCache;
1990
2067
  code: string;
2068
+ define?: UserConfig["define"] | undefined;
1991
2069
  filename: string;
1992
2070
  root?: string | undefined;
1993
2071
  serverOutput: ServerOutputMode;
@@ -1995,11 +2073,13 @@ async function buildServerComponentBundleArtifactCode(options: {
1995
2073
  }): Promise<string> {
1996
2074
  return await bundleAppRouterSourceModule({
1997
2075
  code: options.code,
2076
+ define: options.define,
1998
2077
  label: `server-component:${options.filename}`,
1999
2078
  resolveDir: dirname(options.filename),
2000
2079
  root: options.root,
2001
2080
  serverSourceTransform: {
2002
2081
  clientRouteInferenceCache: options.clientRouteInferenceCache,
2082
+ define: options.define,
2003
2083
  dev: false,
2004
2084
  serverOutput: options.serverOutput,
2005
2085
  vitePlugins: options.vitePlugins,
@@ -2138,6 +2218,7 @@ async function buildRequestModuleArtifactCode(options: {
2138
2218
  appDir: string;
2139
2219
  bundleCache: Map<string, Promise<RouterBundleOutput>>;
2140
2220
  cacheDir?: string | undefined;
2221
+ define?: UserConfig["define"] | undefined;
2141
2222
  filename: string;
2142
2223
  importPolicy: AppRouterImportPolicy;
2143
2224
  routeKind?: AppRoute["kind"] | undefined;
@@ -2148,6 +2229,7 @@ async function buildRequestModuleArtifactCode(options: {
2148
2229
  return await bundleMiddlewareModuleCode({
2149
2230
  appDir: options.appDir,
2150
2231
  code: options.source,
2232
+ define: options.define,
2151
2233
  file: options.filename,
2152
2234
  importPolicy: options.importPolicy,
2153
2235
  vitePlugins: options.vitePlugins,
@@ -2157,6 +2239,7 @@ async function buildRequestModuleArtifactCode(options: {
2157
2239
  if (options.routeKind === "server" || options.routeKind === "metadata") {
2158
2240
  return await bundleAppRouterSourceModule({
2159
2241
  code: options.source,
2242
+ define: options.define,
2160
2243
  label: `server-route:${options.filename}`,
2161
2244
  plugins: [fileImportMetaUrlPlugin()],
2162
2245
  resolveDir: dirname(options.filename),
@@ -2171,6 +2254,7 @@ async function buildRequestModuleArtifactCode(options: {
2171
2254
  bundleCache: options.bundleCache,
2172
2255
  cacheDir: options.cacheDir,
2173
2256
  code: stripRouteRequestOnlyExports(options.source, options.filename),
2257
+ define: options.define,
2174
2258
  filename: options.filename,
2175
2259
  importPolicy: options.importPolicy,
2176
2260
  vitePlugins: options.vitePlugins,
@@ -2182,6 +2266,7 @@ async function bundleRouteLoaderModuleCode(options: {
2182
2266
  bundleCache?: Map<string, Promise<RouterBundleOutput>> | undefined;
2183
2267
  cacheDir?: string | undefined;
2184
2268
  code: string;
2269
+ define?: UserConfig["define"] | undefined;
2185
2270
  filename: string;
2186
2271
  importPolicy?: AppRouterImportPolicy | undefined;
2187
2272
  vitePlugins?: readonly PluginOption[] | undefined;
@@ -2196,6 +2281,7 @@ async function bundleRouteRequestModuleBatchCode(options: {
2196
2281
  appDir: string;
2197
2282
  bundleCache?: Map<string, Promise<RouterBundleOutput>> | undefined;
2198
2283
  cacheDir?: string | undefined;
2284
+ define?: UserConfig["define"] | undefined;
2199
2285
  entries: readonly RouteRequestModuleBatchEntry[];
2200
2286
  importPolicy?: AppRouterImportPolicy | undefined;
2201
2287
  vitePlugins?: readonly PluginOption[] | undefined;
@@ -2218,6 +2304,7 @@ async function bundleRouteRequestModuleBatchCode(options: {
2218
2304
  bundleCache: options.bundleCache,
2219
2305
  cacheDir: options.cacheDir,
2220
2306
  code: entry.code,
2307
+ define: options.define,
2221
2308
  filename: entry.filename,
2222
2309
  importPolicy: options.importPolicy,
2223
2310
  label: entry.label,
@@ -2242,6 +2329,7 @@ async function bundleRouteRequestModuleBatchCode(options: {
2242
2329
  name: namesByKey.get(entry.key) ?? hashText(entry.key).slice(0, 8),
2243
2330
  })),
2244
2331
  entryFileNames: "request/[name].js",
2332
+ define: options.define,
2245
2333
  platform: "node",
2246
2334
  plugins: [
2247
2335
  fileImportMetaUrlPlugin(),
@@ -2267,6 +2355,7 @@ async function bundleRouteRequestModuleBatchCode(options: {
2267
2355
  bundleCache: options.bundleCache,
2268
2356
  cacheDir: options.cacheDir,
2269
2357
  code: entry.code,
2358
+ define: options.define,
2270
2359
  filename: entry.filename,
2271
2360
  importPolicy: options.importPolicy,
2272
2361
  label: entry.label,
@@ -2295,6 +2384,7 @@ async function bundleRouteRequestModuleBatchCode(options: {
2295
2384
  export async function __bundleRouteRequestModuleBatchForTests(options: {
2296
2385
  appDir: string;
2297
2386
  cacheDir?: string | undefined;
2387
+ define?: UserConfig["define"] | undefined;
2298
2388
  entries: readonly RouteRequestModuleBatchEntry[];
2299
2389
  importPolicy?: AppRouterImportPolicy | undefined;
2300
2390
  vitePlugins?: readonly PluginOption[] | undefined;
@@ -2303,6 +2393,7 @@ export async function __bundleRouteRequestModuleBatchForTests(options: {
2303
2393
  await bundleRouteRequestModuleBatchCode({
2304
2394
  appDir: options.appDir,
2305
2395
  cacheDir: options.cacheDir,
2396
+ define: options.define,
2306
2397
  entries: options.entries,
2307
2398
  importPolicy: options.importPolicy,
2308
2399
  vitePlugins: options.vitePlugins,
@@ -2315,6 +2406,7 @@ async function bundleRouteRequestModuleCode(options: {
2315
2406
  bundleCache?: Map<string, Promise<RouterBundleOutput>> | undefined;
2316
2407
  cacheDir?: string | undefined;
2317
2408
  code: string;
2409
+ define?: UserConfig["define"] | undefined;
2318
2410
  filename: string;
2319
2411
  importPolicy?: AppRouterImportPolicy | undefined;
2320
2412
  label: "Loader" | "Metadata";
@@ -2325,6 +2417,7 @@ async function bundleRouteRequestModuleCode(options: {
2325
2417
  cacheDir: options.cacheDir,
2326
2418
  cacheKey: routeRequestBundleCacheKey(options),
2327
2419
  code: options.code,
2420
+ define: options.define,
2328
2421
  filename: options.filename,
2329
2422
  platform: "node",
2330
2423
  root: options.importPolicy?.projectRoot,
@@ -2350,6 +2443,7 @@ async function bundleRouteRequestModuleCode(options: {
2350
2443
  function routeRequestBundleCacheKey(options: {
2351
2444
  appDir: string;
2352
2445
  code: string;
2446
+ define?: UserConfig["define"] | undefined;
2353
2447
  filename: string;
2354
2448
  importPolicy?: AppRouterImportPolicy | undefined;
2355
2449
  label: "Loader" | "Metadata";
@@ -2375,6 +2469,7 @@ function routeRequestBundleCacheKey(options: {
2375
2469
  label: options.label,
2376
2470
  platform: "node",
2377
2471
  target: "es2022",
2472
+ viteDefine: viteDefineCacheKey(options.define),
2378
2473
  vitePlugins: vitePluginsCacheKey(options.vitePlugins),
2379
2474
  });
2380
2475
  }
@@ -3540,7 +3635,12 @@ function cloudflareHydrationMarkerParts(props) {
3540
3635
  }
3541
3636
 
3542
3637
  function escapeScriptJson(json) {
3543
- return json.replaceAll("<", "\\\\u003c");
3638
+ return json
3639
+ .replaceAll("&", "\\\\u0026")
3640
+ .replaceAll("<", "\\\\u003c")
3641
+ .replaceAll(">", "\\\\u003e")
3642
+ .replaceAll("\\u2028", "\\\\u2028")
3643
+ .replaceAll("\\u2029", "\\\\u2029");
3544
3644
  }
3545
3645
 
3546
3646
  function cloudflareRouteIdForPath(path) {
@@ -4387,6 +4487,11 @@ interface ClientStyleManifestEntry {
4387
4487
  file: string;
4388
4488
  }
4389
4489
 
4490
+ interface RouteCssAssetBatch {
4491
+ assets: string[];
4492
+ css: string[];
4493
+ }
4494
+
4390
4495
  async function writeClientRouteBundles(options: {
4391
4496
  appDir: string;
4392
4497
  assetBaseUrl?: string | undefined;
@@ -4414,6 +4519,7 @@ async function writeClientRouteBundles(options: {
4414
4519
  );
4415
4520
  const routeCssAssets = await writeRouteCssAssetBatches({
4416
4521
  appDir: options.appDir,
4522
+ assetBaseUrl: options.assetBaseUrl,
4417
4523
  cacheDir: options.cacheDir,
4418
4524
  clientDir: options.clientDir,
4419
4525
  pageRoutes,
@@ -4423,6 +4529,7 @@ async function writeClientRouteBundles(options: {
4423
4529
  });
4424
4530
  const specialCssAssets = await writeSpecialRouteCssAssetBatches({
4425
4531
  appDir: options.appDir,
4532
+ assetBaseUrl: options.assetBaseUrl,
4426
4533
  cacheDir: options.cacheDir,
4427
4534
  clientDir: options.clientDir,
4428
4535
  projectRoot: options.projectRoot,
@@ -4437,7 +4544,7 @@ async function writeClientRouteBundles(options: {
4437
4544
  };
4438
4545
  }
4439
4546
 
4440
- const css = routeCssAssets.get(route.file) ?? [];
4547
+ const css = routeCssAssets.byRoute.get(route.file) ?? [];
4441
4548
  const source = buildSourceAnalysisForFile(
4442
4549
  options.sourceAnalysis,
4443
4550
  options.projectRoot,
@@ -4510,9 +4617,9 @@ async function writeClientRouteBundles(options: {
4510
4617
 
4511
4618
  if (clientEntries.length === 0) {
4512
4619
  return {
4513
- assets: [],
4620
+ assets: [...routeCssAssets.assets, ...specialCssAssets.assets].sort(),
4514
4621
  routes: entries.flatMap((entry) => ("manifest" in entry ? [entry.manifest] : [])),
4515
- styles: specialCssAssets,
4622
+ styles: specialCssAssets.styles,
4516
4623
  };
4517
4624
  }
4518
4625
 
@@ -4578,7 +4685,10 @@ async function writeClientRouteBundles(options: {
4578
4685
  await writeFile(join(options.clientDir, asset.fileName), source);
4579
4686
  }
4580
4687
 
4581
- const generatedAssets = new Set<string>();
4688
+ const generatedAssets = new Set<string>([
4689
+ ...routeCssAssets.assets,
4690
+ ...specialCssAssets.assets,
4691
+ ]);
4582
4692
 
4583
4693
  for (const chunk of output.chunks) {
4584
4694
  generatedAssets.add(chunk.fileName);
@@ -4632,18 +4742,19 @@ async function writeClientRouteBundles(options: {
4632
4742
  return {
4633
4743
  assets: Array.from(generatedAssets).sort(),
4634
4744
  routes,
4635
- styles: specialCssAssets,
4745
+ styles: specialCssAssets.styles,
4636
4746
  };
4637
4747
  }
4638
4748
 
4639
4749
  async function writeSpecialRouteCssAssetBatches(options: {
4640
4750
  appDir: string;
4751
+ assetBaseUrl?: string | undefined;
4641
4752
  cacheDir?: string | undefined;
4642
4753
  clientDir: string;
4643
4754
  projectRoot: string;
4644
4755
  sourceAnalysis: BuildSourceAnalysisScope;
4645
4756
  vitePlugins?: readonly PluginOption[] | undefined;
4646
- }): Promise<ClientStyleManifestEntry[]> {
4757
+ }): Promise<{ assets: string[]; styles: ClientStyleManifestEntry[] }> {
4647
4758
  const boundaryFiles = await collectSpecialBoundaryFiles(options.appDir);
4648
4759
  const entries = await mapWithBuildConcurrency(boundaryFiles, async (file) => {
4649
4760
  const cssFiles = await collectRouteCssFilesFromSources({
@@ -4658,7 +4769,8 @@ async function writeSpecialRouteCssAssetBatches(options: {
4658
4769
  return undefined;
4659
4770
  }
4660
4771
 
4661
- const css = await writeRouteCssAssetsForFiles({
4772
+ const batch = await writeRouteCssAssetsForFiles({
4773
+ assetBaseUrl: options.assetBaseUrl,
4662
4774
  cacheDir: options.cacheDir,
4663
4775
  clientDir: options.clientDir,
4664
4776
  cssFiles,
@@ -4668,17 +4780,23 @@ async function writeSpecialRouteCssAssetBatches(options: {
4668
4780
  vitePlugins: options.vitePlugins,
4669
4781
  });
4670
4782
 
4671
- return css.length === 0
4783
+ return batch.css.length === 0
4672
4784
  ? undefined
4673
4785
  : ({
4674
- css,
4675
- file: relative(options.appDir, file).split(sep).join("/"),
4676
- } satisfies ClientStyleManifestEntry);
4786
+ assets: batch.assets,
4787
+ style: {
4788
+ css: batch.css,
4789
+ file: relative(options.appDir, file).split(sep).join("/"),
4790
+ } satisfies ClientStyleManifestEntry,
4791
+ });
4677
4792
  });
4678
4793
 
4679
- return entries
4680
- .filter((entry): entry is ClientStyleManifestEntry => entry !== undefined)
4681
- .sort((left, right) => left.file.localeCompare(right.file));
4794
+ return {
4795
+ assets: entries.flatMap((entry) => entry?.assets ?? []).sort(),
4796
+ styles: entries
4797
+ .flatMap((entry) => (entry === undefined ? [] : [entry.style]))
4798
+ .sort((left, right) => left.file.localeCompare(right.file)),
4799
+ };
4682
4800
  }
4683
4801
 
4684
4802
  function specialBoundaryRouteId(appDir: string, file: string): string {
@@ -4693,13 +4811,14 @@ function specialBoundaryRouteId(appDir: string, file: string): string {
4693
4811
 
4694
4812
  async function writeRouteCssAssetBatches(options: {
4695
4813
  appDir: string;
4814
+ assetBaseUrl?: string | undefined;
4696
4815
  cacheDir?: string | undefined;
4697
4816
  clientDir: string;
4698
4817
  pageRoutes: readonly (AppRoute & { kind: "page" })[];
4699
4818
  projectRoot: string;
4700
4819
  sourceAnalysis: BuildSourceAnalysisScope;
4701
4820
  vitePlugins?: readonly PluginOption[] | undefined;
4702
- }): Promise<Map<string, string[]>> {
4821
+ }): Promise<{ assets: string[]; byRoute: Map<string, string[]> }> {
4703
4822
  const cssInputs = await mapWithBuildConcurrency(options.pageRoutes, async (route) => {
4704
4823
  const cssFiles = await collectRouteCssFilesFromSources({
4705
4824
  appDir: options.appDir,
@@ -4734,6 +4853,7 @@ async function writeRouteCssAssetBatches(options: {
4734
4853
  [
4735
4854
  key,
4736
4855
  await writeRouteCssAssetsForFiles({
4856
+ assetBaseUrl: options.assetBaseUrl,
4737
4857
  cacheDir: options.cacheDir,
4738
4858
  clientDir: options.clientDir,
4739
4859
  cssFiles: group.cssFiles,
@@ -4746,19 +4866,26 @@ async function writeRouteCssAssetBatches(options: {
4746
4866
  );
4747
4867
  const cssByGroup = new Map(writtenGroups);
4748
4868
  const cssByRoute = new Map<string, string[]>();
4869
+ const assets = new Set<string>();
4749
4870
 
4750
4871
  for (const [key, group] of groups) {
4751
- const css = cssByGroup.get(key) ?? [];
4872
+ const batch = cssByGroup.get(key);
4873
+ const css = batch?.css ?? [];
4874
+
4875
+ for (const asset of batch?.assets ?? []) {
4876
+ assets.add(asset);
4877
+ }
4752
4878
 
4753
4879
  for (const routeFile of group.routeFiles) {
4754
4880
  cssByRoute.set(routeFile, css);
4755
4881
  }
4756
4882
  }
4757
4883
 
4758
- return cssByRoute;
4884
+ return { assets: [...assets].sort(), byRoute: cssByRoute };
4759
4885
  }
4760
4886
 
4761
4887
  async function writeRouteCssAssetsForFiles(options: {
4888
+ assetBaseUrl?: string | undefined;
4762
4889
  cacheDir?: string | undefined;
4763
4890
  clientDir: string;
4764
4891
  cssFiles: readonly string[];
@@ -4766,10 +4893,10 @@ async function writeRouteCssAssetsForFiles(options: {
4766
4893
  projectRoot: string;
4767
4894
  routeIds: readonly string[];
4768
4895
  vitePlugins?: readonly PluginOption[] | undefined;
4769
- }): Promise<string[]> {
4896
+ }): Promise<RouteCssAssetBatch> {
4770
4897
  const cssFiles = [...options.cssFiles];
4771
4898
  if (cssFiles.length === 0) {
4772
- return [];
4899
+ return { assets: [], css: [] };
4773
4900
  }
4774
4901
 
4775
4902
  const code = [
@@ -4777,6 +4904,7 @@ async function writeRouteCssAssetsForFiles(options: {
4777
4904
  "export default undefined;",
4778
4905
  ].join("\n");
4779
4906
  const output = await bundleRouterModule({
4907
+ base: options.assetBaseUrl ?? "/_mreact/client/",
4780
4908
  cacheDir: options.cacheDir,
4781
4909
  code,
4782
4910
  filename: options.pageFile,
@@ -4786,6 +4914,8 @@ async function writeRouteCssAssetsForFiles(options: {
4786
4914
  vitePlugins: options.vitePlugins,
4787
4915
  });
4788
4916
  const cssAssets = (output.assets ?? []).filter((asset) => asset.fileName.endsWith(".css"));
4917
+ const nonCssAssets = (output.assets ?? []).filter((asset) => !asset.fileName.endsWith(".css"));
4918
+ const writtenAssets: string[] = [];
4789
4919
  const written: string[] = [];
4790
4920
  const routeStem =
4791
4921
  options.routeIds.length === 1
@@ -4803,7 +4933,16 @@ async function writeRouteCssAssetsForFiles(options: {
4803
4933
  written.push(cssFile);
4804
4934
  }
4805
4935
 
4806
- return written;
4936
+ for (const asset of nonCssAssets) {
4937
+ await mkdir(dirname(join(options.clientDir, asset.fileName)), { recursive: true });
4938
+ await writeFile(
4939
+ join(options.clientDir, asset.fileName),
4940
+ typeof asset.source === "string" ? asset.source : Buffer.from(asset.source),
4941
+ );
4942
+ writtenAssets.push(asset.fileName);
4943
+ }
4944
+
4945
+ return { assets: writtenAssets.sort(), css: written };
4807
4946
  }
4808
4947
 
4809
4948
  function applyClientSourceMapReference(options: {
@@ -5079,7 +5218,13 @@ async function copyCloudflarePagesPublicAssets(options: {
5079
5218
  publicAssets: readonly string[];
5080
5219
  }): Promise<void> {
5081
5220
  for (const asset of options.publicAssets) {
5082
- if (!asset.startsWith("/") || asset.startsWith("//") || asset.includes("..")) {
5221
+ if (
5222
+ !asset.startsWith("/") ||
5223
+ asset.startsWith("//") ||
5224
+ asset.includes("..") ||
5225
+ asset === "/_mreact" ||
5226
+ asset.startsWith("/_mreact/")
5227
+ ) {
5083
5228
  continue;
5084
5229
  }
5085
5230
 
@@ -5234,7 +5379,10 @@ async function collectBuildFiles(
5234
5379
  }
5235
5380
 
5236
5381
  function isProductionBuildIgnoredSourceFile(relativeFile: string): boolean {
5237
- return /(?:^|[/\\])[^/\\]+\.(?:spec|test)\.[cm]?[jt]sx?$/u.test(relativeFile);
5382
+ return (
5383
+ /(?:^|[/\\])__tests__(?:[/\\]|$)/u.test(relativeFile) ||
5384
+ /(?:^|[/\\])[^/\\]+\.(?:spec|test)\.[cm]?[jt]sx?$/u.test(relativeFile)
5385
+ );
5238
5386
  }
5239
5387
 
5240
5388
  function isAppFileConventionAsset(file: string, appDir: string): boolean {