@react-router/dev 0.0.0-experimental-f887449bb → 0.0.0-experimental-e067e5e52

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/CHANGELOG.md CHANGED
@@ -1,5 +1,62 @@
1
1
  # `@react-router/dev`
2
2
 
3
+ ## 7.13.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Bump @remix-run/node-fetch-server dep ([#14704](https://github.com/remix-run/react-router/pull/14704))
8
+ - Updated dependencies:
9
+ - `react-router@7.13.0`
10
+ - `@react-router/node@7.13.0`
11
+ - `@react-router/serve@7.13.0`
12
+
13
+ ## 7.12.0
14
+
15
+ ### Minor Changes
16
+
17
+ - Add additional layer of CSRF protection by rejecting submissions to UI routes from external origins. If you need to permit access to specific external origins, you can specify them in the `react-router.config.ts` config `allowedActionOrigins` field. ([#14708](https://github.com/remix-run/react-router/pull/14708))
18
+
19
+ ### Patch Changes
20
+
21
+ - Fix `Maximum call stack size exceeded` errors when HMR is triggered against code with cyclic imports ([#14522](https://github.com/remix-run/react-router/pull/14522))
22
+
23
+ - fix(vite): Skip SSR middleware in preview server for SPA mode ([#14673](https://github.com/remix-run/react-router/pull/14673))
24
+
25
+ - \[UNSTABLE] Add a new `future.unstable_trailingSlashAwareDataRequests` flag to provide consistent behavior of `request.pathname` inside `middleware`, `loader`, and `action` functions on document and data requests when a trailing slash is present in the browser URL. ([#14644](https://github.com/remix-run/react-router/pull/14644))
26
+
27
+ Currently, your HTTP and `request` pathnames would be as follows for `/a/b/c` and `/a/b/c/`
28
+
29
+ | URL `/a/b/c` | **HTTP pathname** | **`request` pathname\`** |
30
+ | ------------ | ----------------- | ------------------------ |
31
+ | **Document** | `/a/b/c` | `/a/b/c` ✅ |
32
+ | **Data** | `/a/b/c.data` | `/a/b/c` ✅ |
33
+
34
+ | URL `/a/b/c/` | **HTTP pathname** | **`request` pathname\`** |
35
+ | ------------- | ----------------- | ------------------------ |
36
+ | **Document** | `/a/b/c/` | `/a/b/c/` ✅ |
37
+ | **Data** | `/a/b/c.data` | `/a/b/c` ⚠️ |
38
+
39
+ With this flag enabled, these pathnames will be made consistent though a new `_.data` format for client-side `.data` requests:
40
+
41
+ | URL `/a/b/c` | **HTTP pathname** | **`request` pathname\`** |
42
+ | ------------ | ----------------- | ------------------------ |
43
+ | **Document** | `/a/b/c` | `/a/b/c` ✅ |
44
+ | **Data** | `/a/b/c.data` | `/a/b/c` ✅ |
45
+
46
+ | URL `/a/b/c/` | **HTTP pathname** | **`request` pathname\`** |
47
+ | ------------- | ------------------ | ------------------------ |
48
+ | **Document** | `/a/b/c/` | `/a/b/c/` ✅ |
49
+ | **Data** | `/a/b/c/_.data` ⬅️ | `/a/b/c/` ✅ |
50
+
51
+ This a bug fix but we are putting it behind an opt-in flag because it has the potential to be a "breaking bug fix" if you are relying on the URL format for any other application or caching logic.
52
+
53
+ Enabling this flag also changes the format of client side `.data` requests from `/_root.data` to `/_.data` when navigating to `/` to align with the new format. This does not impact the `request` pathname which is still `/` in all cases.
54
+
55
+ - Updated dependencies:
56
+ - `react-router@7.12.0`
57
+ - `@react-router/node@7.12.0`
58
+ - `@react-router/serve@7.12.0`
59
+
3
60
  ## 7.11.0
4
61
 
5
62
  ### Minor Changes
package/dist/cli/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @react-router/dev v0.0.0-experimental-f887449bb
3
+ * @react-router/dev v0.0.0-experimental-e067e5e52
4
4
  *
5
5
  * Copyright (c) Remix Software Inc.
6
6
  *
@@ -217,7 +217,7 @@ function validateRouteConfig({
217
217
  `Route config in "${routeConfigFile}" is invalid.`,
218
218
  root ? `${root}` : [],
219
219
  nested ? Object.entries(nested).map(
220
- ([path9, message]) => `Path: routes.${path9}
220
+ ([path10, message]) => `Path: routes.${path10}
221
221
  ${message}`
222
222
  ) : []
223
223
  ].flat().join("\n\n")
@@ -376,7 +376,7 @@ async function resolveConfig({
376
376
  basename: basename3,
377
377
  buildDirectory: userBuildDirectory,
378
378
  buildEnd,
379
- prerender,
379
+ prerender: prerender2,
380
380
  routeDiscovery: userRouteDiscovery,
381
381
  serverBuildFile,
382
382
  serverBundles,
@@ -390,15 +390,15 @@ async function resolveConfig({
390
390
  if (!ssr && serverBundles) {
391
391
  serverBundles = void 0;
392
392
  }
393
- if (prerender) {
393
+ if (prerender2) {
394
394
  let isValidPrerenderPathsConfig = (p) => typeof p === "boolean" || typeof p === "function" || Array.isArray(p);
395
- let isValidPrerenderConfig = isValidPrerenderPathsConfig(prerender) || typeof prerender === "object" && "paths" in prerender && isValidPrerenderPathsConfig(prerender.paths);
395
+ let isValidPrerenderConfig = isValidPrerenderPathsConfig(prerender2) || typeof prerender2 === "object" && "paths" in prerender2 && isValidPrerenderPathsConfig(prerender2.paths);
396
396
  if (!isValidPrerenderConfig) {
397
397
  return err(
398
398
  "The `prerender`/`prerender.paths` config must be a boolean, an array of string paths, or a function returning a boolean or array of string paths."
399
399
  );
400
400
  }
401
- let isValidConcurrencyConfig = typeof prerender != "object" || !("unstable_concurrency" in prerender) || typeof prerender.unstable_concurrency === "number" && Number.isInteger(prerender.unstable_concurrency) && prerender.unstable_concurrency > 0;
401
+ let isValidConcurrencyConfig = typeof prerender2 != "object" || !("unstable_concurrency" in prerender2) || typeof prerender2.unstable_concurrency === "number" && Number.isInteger(prerender2.unstable_concurrency) && prerender2.unstable_concurrency > 0;
402
402
  if (!isValidConcurrencyConfig) {
403
403
  return err(
404
404
  "The `prerender.unstable_concurrency` config must be a positive integer if specified."
@@ -507,23 +507,26 @@ async function resolveConfig({
507
507
  unstable_optimizeDeps: userAndPresetConfigs.future?.unstable_optimizeDeps ?? false,
508
508
  unstable_subResourceIntegrity: userAndPresetConfigs.future?.unstable_subResourceIntegrity ?? false,
509
509
  unstable_trailingSlashAwareDataRequests: userAndPresetConfigs.future?.unstable_trailingSlashAwareDataRequests ?? false,
510
+ unstable_previewServerPrerendering: userAndPresetConfigs.future?.unstable_previewServerPrerendering ?? false,
510
511
  v8_middleware: userAndPresetConfigs.future?.v8_middleware ?? false,
511
512
  v8_splitRouteModules: userAndPresetConfigs.future?.v8_splitRouteModules ?? false,
512
513
  v8_viteEnvironmentApi: userAndPresetConfigs.future?.v8_viteEnvironmentApi ?? false
513
514
  };
515
+ let allowedActionOrigins = userAndPresetConfigs.allowedActionOrigins ?? false;
514
516
  let reactRouterConfig = deepFreeze({
515
517
  appDirectory,
516
518
  basename: basename3,
517
519
  buildDirectory,
518
520
  buildEnd,
519
521
  future,
520
- prerender,
522
+ prerender: prerender2,
521
523
  routes: routes2,
522
524
  routeDiscovery,
523
525
  serverBuildFile,
524
526
  serverBundles,
525
527
  serverModuleFormat,
526
528
  ssr,
529
+ allowedActionOrigins,
527
530
  unstable_routeConfig: routeConfig
528
531
  });
529
532
  for (let preset of reactRouterUserConfig.presets ?? []) {
@@ -583,11 +586,11 @@ async function createConfigLoader({
583
586
  if (!fsWatcher) {
584
587
  fsWatcher = import_chokidar.default.watch([root, appDirectory], {
585
588
  ignoreInitial: true,
586
- ignored: (path9) => {
587
- let dirname5 = import_pathe3.default.dirname(path9);
589
+ ignored: (path10) => {
590
+ let dirname5 = import_pathe3.default.dirname(path10);
588
591
  return !dirname5.startsWith(appDirectory) && // Ensure we're only watching files outside of the app directory
589
592
  // that are at the root level, not nested in subdirectories
590
- path9 !== root && // Watch the root directory itself
593
+ path10 !== root && // Watch the root directory itself
591
594
  dirname5 !== root;
592
595
  }
593
596
  });
@@ -912,7 +915,7 @@ function fullpath(lineage2) {
912
915
  if (lineage2.length === 1 && route?.id === "root") return "/";
913
916
  const isLayout = route && route.index !== true && route.path === void 0;
914
917
  if (isLayout) return void 0;
915
- return "/" + lineage2.map((route2) => route2.path?.replace(/^\//, "")?.replace(/\/$/, "")).filter((path9) => path9 !== void 0 && path9 !== "").join("/");
918
+ return "/" + lineage2.map((route2) => route2.path?.replace(/^\//, "")?.replace(/\/$/, "")).filter((path10) => path10 !== void 0 && path10 !== "").join("/");
916
919
  }
917
920
  var init_route = __esm({
918
921
  "typegen/route.ts"() {
@@ -957,6 +960,7 @@ function generateServerBuild(ctx) {
957
960
  export const routeDiscovery: ServerBuild["routeDiscovery"];
958
961
  export const routes: ServerBuild["routes"];
959
962
  export const ssr: ServerBuild["ssr"];
963
+ export const allowedActionOrigins: ServerBuild["allowedActionOrigins"];
960
964
  export const unstable_getCriticalCss: ServerBuild["unstable_getCriticalCss"];
961
965
  }
962
966
  `;
@@ -1095,8 +1099,8 @@ function routeModulesType(ctx) {
1095
1099
  );
1096
1100
  }
1097
1101
  function isInAppDirectory(ctx, routeFile) {
1098
- const path9 = Path3.resolve(ctx.config.appDirectory, routeFile);
1099
- return path9.startsWith(ctx.config.appDirectory);
1102
+ const path10 = Path3.resolve(ctx.config.appDirectory, routeFile);
1103
+ return path10.startsWith(ctx.config.appDirectory);
1100
1104
  }
1101
1105
  function getRouteAnnotations({
1102
1106
  ctx,
@@ -1208,21 +1212,21 @@ function getRouteAnnotations({
1208
1212
  return { filename: filename2, content };
1209
1213
  }
1210
1214
  function relativeImportSource(from, to) {
1211
- let path9 = Path3.relative(Path3.dirname(from), to);
1212
- let extension = Path3.extname(path9);
1213
- path9 = Path3.join(Path3.dirname(path9), Pathe.filename(path9));
1214
- if (!path9.startsWith("../")) path9 = "./" + path9;
1215
+ let path10 = Path3.relative(Path3.dirname(from), to);
1216
+ let extension = Path3.extname(path10);
1217
+ path10 = Path3.join(Path3.dirname(path10), Pathe.filename(path10));
1218
+ if (!path10.startsWith("../")) path10 = "./" + path10;
1215
1219
  if (!extension || /\.(js|ts)x?$/.test(extension)) {
1216
1220
  extension = ".js";
1217
1221
  }
1218
- return path9 + extension;
1222
+ return path10 + extension;
1219
1223
  }
1220
1224
  function rootDirsPath(ctx, typesPath) {
1221
1225
  const rel = Path3.relative(typesDirectory(ctx), typesPath);
1222
1226
  return Path3.join(ctx.rootDirectory, rel);
1223
1227
  }
1224
- function paramsType(path9) {
1225
- const params = parse2(path9);
1228
+ function paramsType(path10) {
1229
+ const params = parse2(path10);
1226
1230
  return t2.tsTypeLiteral(
1227
1231
  Object.entries(params).map(([param, isRequired]) => {
1228
1232
  const property = t2.tsPropertySignature(
@@ -1374,11 +1378,9 @@ var init_has_rsc_plugin = __esm({
1374
1378
  });
1375
1379
 
1376
1380
  // vite/node-adapter.ts
1377
- var import_node_fetch_server;
1378
1381
  var init_node_adapter = __esm({
1379
1382
  "vite/node-adapter.ts"() {
1380
1383
  "use strict";
1381
- import_node_fetch_server = require("@remix-run/node-fetch-server");
1382
1384
  init_invariant();
1383
1385
  }
1384
1386
  });
@@ -1554,6 +1556,16 @@ var init_warn_on_client_source_maps = __esm({
1554
1556
  }
1555
1557
  });
1556
1558
 
1559
+ // vite/plugins/prerender.ts
1560
+ var import_promises2, import_node_path2;
1561
+ var init_prerender = __esm({
1562
+ "vite/plugins/prerender.ts"() {
1563
+ "use strict";
1564
+ import_promises2 = require("fs/promises");
1565
+ import_node_path2 = __toESM(require("path"));
1566
+ }
1567
+ });
1568
+
1557
1569
  // vite/plugin.ts
1558
1570
  async function resolveViteConfig({
1559
1571
  configFile,
@@ -1596,11 +1608,11 @@ function getServerBundleIds(ctx) {
1596
1608
  async function cleanBuildDirectory(viteConfig, ctx) {
1597
1609
  let buildDirectory = ctx.reactRouterConfig.buildDirectory;
1598
1610
  let isWithinRoot = () => {
1599
- let relativePath = path7.relative(ctx.rootDirectory, buildDirectory);
1600
- return !relativePath.startsWith("..") && !path7.isAbsolute(relativePath);
1611
+ let relativePath = path8.relative(ctx.rootDirectory, buildDirectory);
1612
+ return !relativePath.startsWith("..") && !path8.isAbsolute(relativePath);
1601
1613
  };
1602
1614
  if (viteConfig.build.emptyOutDir ?? isWithinRoot()) {
1603
- await (0, import_promises2.rm)(buildDirectory, { force: true, recursive: true });
1615
+ await (0, import_promises3.rm)(buildDirectory, { force: true, recursive: true });
1604
1616
  }
1605
1617
  }
1606
1618
  async function cleanViteManifests(environmentsOptions, ctx) {
@@ -1608,7 +1620,7 @@ async function cleanViteManifests(environmentsOptions, ctx) {
1608
1620
  ([environmentName, options]) => {
1609
1621
  let outDir = options.build?.outDir;
1610
1622
  invariant(outDir, `Expected build.outDir for ${environmentName}`);
1611
- return path7.join(outDir, ".vite/manifest.json");
1623
+ return path8.join(outDir, ".vite/manifest.json");
1612
1624
  }
1613
1625
  );
1614
1626
  await Promise.all(
@@ -1616,12 +1628,12 @@ async function cleanViteManifests(environmentsOptions, ctx) {
1616
1628
  let manifestExists = (0, import_node_fs3.existsSync)(viteManifestPath);
1617
1629
  if (!manifestExists) return;
1618
1630
  if (!ctx.viteManifestEnabled) {
1619
- await (0, import_promises2.rm)(viteManifestPath, { force: true, recursive: true });
1631
+ await (0, import_promises3.rm)(viteManifestPath, { force: true, recursive: true });
1620
1632
  }
1621
- let viteDir = path7.dirname(viteManifestPath);
1622
- let viteDirFiles = await (0, import_promises2.readdir)(viteDir, { recursive: true });
1633
+ let viteDir = path8.dirname(viteManifestPath);
1634
+ let viteDirFiles = await (0, import_promises3.readdir)(viteDir, { recursive: true });
1623
1635
  if (viteDirFiles.length === 0) {
1624
- await (0, import_promises2.rm)(viteDir, { force: true, recursive: true });
1636
+ await (0, import_promises3.rm)(viteDir, { force: true, recursive: true });
1625
1637
  }
1626
1638
  })
1627
1639
  );
@@ -1635,10 +1647,10 @@ function mergeEnvironmentOptions(base, ...overrides) {
1635
1647
  }
1636
1648
  async function getEnvironmentOptionsResolvers(ctx, viteCommand) {
1637
1649
  let { serverBuildFile, serverModuleFormat } = ctx.reactRouterConfig;
1638
- let packageRoot = path7.dirname(
1650
+ let packageRoot = path8.dirname(
1639
1651
  require.resolve("@react-router/dev/package.json")
1640
1652
  );
1641
- let { moduleSyncEnabled } = await import(`file:///${path7.join(packageRoot, "module-sync-enabled/index.mjs")}`);
1653
+ let { moduleSyncEnabled } = await import(`file:///${path8.join(packageRoot, "module-sync-enabled/index.mjs")}`);
1642
1654
  let vite2 = getVite();
1643
1655
  function getBaseOptions({
1644
1656
  viteUserConfig
@@ -1717,7 +1729,7 @@ async function getEnvironmentOptionsResolvers(ctx, viteCommand) {
1717
1729
  ctx.entryClientFilePath,
1718
1730
  ...Object.values(ctx.reactRouterConfig.routes).flatMap(
1719
1731
  (route) => {
1720
- let routeFilePath = path7.resolve(
1732
+ let routeFilePath = path8.resolve(
1721
1733
  ctx.reactRouterConfig.appDirectory,
1722
1734
  route.file
1723
1735
  );
@@ -1741,7 +1753,7 @@ async function getEnvironmentOptionsResolvers(ctx, viteCommand) {
1741
1753
  ) : null;
1742
1754
  let routeChunkSuffix = routeChunkName ? `-${(0, import_kebabCase.default)(routeChunkName)}` : "";
1743
1755
  let assetsDir = (ctx.reactRouterConfig.future.v8_viteEnvironmentApi ? viteUserConfig?.environments?.client?.build?.assetsDir : null) ?? viteUserConfig?.build?.assetsDir ?? "assets";
1744
- return path7.posix.join(
1756
+ return path8.posix.join(
1745
1757
  assetsDir,
1746
1758
  `[name]${routeChunkSuffix}-[hash].js`
1747
1759
  );
@@ -1791,17 +1803,16 @@ function resolveEnvironmentsOptions(environmentResolvers, resolverOptions) {
1791
1803
  function isNonNullable(x) {
1792
1804
  return x != null;
1793
1805
  }
1794
- var import_node_crypto, import_node_fs3, import_promises2, path7, url, babel2, import_node_fetch_server2, import_react_router2, import_es_module_lexer, import_pick3, import_jsesc, import_picocolors5, import_kebabCase, CLIENT_NON_COMPONENT_EXPORTS, CLIENT_ROUTE_EXPORTS, BUILD_CLIENT_ROUTE_QUERY_STRING, SSR_BUNDLE_PREFIX, virtualHmrRuntime, virtualInjectHmrRuntime, virtual, getServerBuildDirectory, getClientBuildDirectory, defaultEntriesDir, defaultEntries, REACT_REFRESH_HEADER;
1806
+ var import_node_crypto, import_node_fs3, import_promises3, path8, url, babel2, import_react_router2, import_es_module_lexer, import_pick3, import_jsesc, import_picocolors5, import_kebabCase, CLIENT_NON_COMPONENT_EXPORTS, CLIENT_ROUTE_EXPORTS, BUILD_CLIENT_ROUTE_QUERY_STRING, SSR_BUNDLE_PREFIX, virtualHmrRuntime, virtualInjectHmrRuntime, virtual, getServerBuildDirectory, getClientBuildDirectory, defaultEntriesDir, defaultEntries, REACT_REFRESH_HEADER;
1795
1807
  var init_plugin = __esm({
1796
1808
  "vite/plugin.ts"() {
1797
1809
  "use strict";
1798
1810
  import_node_crypto = require("crypto");
1799
1811
  import_node_fs3 = require("fs");
1800
- import_promises2 = require("fs/promises");
1801
- path7 = __toESM(require("path"));
1812
+ import_promises3 = require("fs/promises");
1813
+ path8 = __toESM(require("path"));
1802
1814
  url = __toESM(require("url"));
1803
1815
  babel2 = __toESM(require("@babel/core"));
1804
- import_node_fetch_server2 = require("@remix-run/node-fetch-server");
1805
1816
  import_react_router2 = require("react-router");
1806
1817
  import_es_module_lexer = require("es-module-lexer");
1807
1818
  import_pick3 = __toESM(require("lodash/pick"));
@@ -1828,6 +1839,7 @@ var init_plugin = __esm({
1828
1839
  init_load_dotenv();
1829
1840
  init_validate_plugin_order();
1830
1841
  init_warn_on_client_source_maps();
1842
+ init_prerender();
1831
1843
  CLIENT_NON_COMPONENT_EXPORTS = [
1832
1844
  "clientAction",
1833
1845
  "clientLoader",
@@ -1853,20 +1865,20 @@ var init_plugin = __esm({
1853
1865
  serverManifest: create("server-manifest"),
1854
1866
  browserManifest: create("browser-manifest")
1855
1867
  };
1856
- getServerBuildDirectory = (reactRouterConfig, { serverBundleId } = {}) => path7.join(
1868
+ getServerBuildDirectory = (reactRouterConfig, { serverBundleId } = {}) => path8.join(
1857
1869
  reactRouterConfig.buildDirectory,
1858
1870
  "server",
1859
1871
  ...serverBundleId ? [serverBundleId] : []
1860
1872
  );
1861
- getClientBuildDirectory = (reactRouterConfig) => path7.join(reactRouterConfig.buildDirectory, "client");
1862
- defaultEntriesDir = path7.resolve(
1863
- path7.dirname(require.resolve("@react-router/dev/package.json")),
1873
+ getClientBuildDirectory = (reactRouterConfig) => path8.join(reactRouterConfig.buildDirectory, "client");
1874
+ defaultEntriesDir = path8.resolve(
1875
+ path8.dirname(require.resolve("@react-router/dev/package.json")),
1864
1876
  "dist",
1865
1877
  "config",
1866
1878
  "defaults"
1867
1879
  );
1868
1880
  defaultEntries = (0, import_node_fs3.readdirSync)(defaultEntriesDir).map(
1869
- (filename2) => path7.join(defaultEntriesDir, filename2)
1881
+ (filename2) => path8.join(defaultEntriesDir, filename2)
1870
1882
  );
1871
1883
  invariant(defaultEntries.length > 0, "No default entries found");
1872
1884
  REACT_REFRESH_HEADER = `
@@ -2151,8 +2163,8 @@ var import_picocolors9 = __toESM(require("picocolors"));
2151
2163
 
2152
2164
  // cli/commands.ts
2153
2165
  var import_node_fs4 = require("fs");
2154
- var import_promises3 = require("fs/promises");
2155
- var path8 = __toESM(require("path"));
2166
+ var import_promises4 = require("fs/promises");
2167
+ var path9 = __toESM(require("path"));
2156
2168
  var import_exit_hook = __toESM(require("exit-hook"));
2157
2169
  var import_picocolors8 = __toESM(require("picocolors"));
2158
2170
  var import_react_router3 = require("react-router");
@@ -2325,14 +2337,14 @@ async function generateEntry(entry, rootDirectory, flags = {}) {
2325
2337
  console.error(import_picocolors8.default.red(`No default server entry detected.`));
2326
2338
  return;
2327
2339
  }
2328
- let defaultsDirectory = path8.resolve(
2329
- path8.dirname(require.resolve("@react-router/dev/package.json")),
2340
+ let defaultsDirectory = path9.resolve(
2341
+ path9.dirname(require.resolve("@react-router/dev/package.json")),
2330
2342
  "dist",
2331
2343
  "config",
2332
2344
  "defaults"
2333
2345
  );
2334
- let defaultEntryClient = path8.resolve(defaultsDirectory, "entry.client.tsx");
2335
- let defaultEntryServer = path8.resolve(
2346
+ let defaultEntryClient = path9.resolve(defaultsDirectory, "entry.client.tsx");
2347
+ let defaultEntryServer = path9.resolve(
2336
2348
  defaultsDirectory,
2337
2349
  `entry.server.node.tsx`
2338
2350
  );
@@ -2341,19 +2353,19 @@ async function generateEntry(entry, rootDirectory, flags = {}) {
2341
2353
  let useTypeScript = flags.typescript ?? true;
2342
2354
  let outputExtension = useTypeScript ? "tsx" : "jsx";
2343
2355
  let outputEntry = `${entry}.${outputExtension}`;
2344
- let outputFile = path8.resolve(appDirectory, outputEntry);
2356
+ let outputFile = path9.resolve(appDirectory, outputEntry);
2345
2357
  if (!useTypeScript) {
2346
2358
  let javascript = await transpile(contents, {
2347
2359
  cwd: rootDirectory,
2348
2360
  filename: isServerEntry ? defaultEntryServer : defaultEntryClient
2349
2361
  });
2350
- await (0, import_promises3.writeFile)(outputFile, javascript, "utf-8");
2362
+ await (0, import_promises4.writeFile)(outputFile, javascript, "utf-8");
2351
2363
  } else {
2352
- await (0, import_promises3.writeFile)(outputFile, contents, "utf-8");
2364
+ await (0, import_promises4.writeFile)(outputFile, contents, "utf-8");
2353
2365
  }
2354
2366
  console.log(
2355
2367
  import_picocolors8.default.blue(
2356
- `Entry file ${entry} created at ${path8.relative(
2368
+ `Entry file ${entry} created at ${path9.relative(
2357
2369
  rootDirectory,
2358
2370
  outputFile
2359
2371
  )}.`
@@ -2362,16 +2374,16 @@ async function generateEntry(entry, rootDirectory, flags = {}) {
2362
2374
  }
2363
2375
  function resolveRootDirectory(root, flags) {
2364
2376
  if (root) {
2365
- return path8.resolve(root);
2377
+ return path9.resolve(root);
2366
2378
  }
2367
- return process.env.REACT_ROUTER_ROOT || (flags?.config ? path8.dirname(path8.resolve(flags.config)) : process.cwd());
2379
+ return process.env.REACT_ROUTER_ROOT || (flags?.config ? path9.dirname(path9.resolve(flags.config)) : process.cwd());
2368
2380
  }
2369
2381
  async function checkForEntry(rootDirectory, appDirectory, entries2) {
2370
2382
  for (let entry of entries2) {
2371
- let entryPath = path8.resolve(appDirectory, entry);
2383
+ let entryPath = path9.resolve(appDirectory, entry);
2372
2384
  let exists = (0, import_node_fs4.existsSync)(entryPath);
2373
2385
  if (exists) {
2374
- let relative7 = path8.relative(rootDirectory, entryPath);
2386
+ let relative7 = path9.relative(rootDirectory, entryPath);
2375
2387
  console.error(import_picocolors8.default.red(`Entry file ${relative7} already exists.`));
2376
2388
  return process.exit(1);
2377
2389
  }
@@ -2379,12 +2391,12 @@ async function checkForEntry(rootDirectory, appDirectory, entries2) {
2379
2391
  }
2380
2392
  async function createServerEntry(rootDirectory, appDirectory, inputFile) {
2381
2393
  await checkForEntry(rootDirectory, appDirectory, serverEntries);
2382
- let contents = await (0, import_promises3.readFile)(inputFile, "utf-8");
2394
+ let contents = await (0, import_promises4.readFile)(inputFile, "utf-8");
2383
2395
  return contents;
2384
2396
  }
2385
2397
  async function createClientEntry(rootDirectory, appDirectory, inputFile) {
2386
2398
  await checkForEntry(rootDirectory, appDirectory, clientEntries);
2387
- let contents = await (0, import_promises3.readFile)(inputFile, "utf-8");
2399
+ let contents = await (0, import_promises4.readFile)(inputFile, "utf-8");
2388
2400
  return contents;
2389
2401
  }
2390
2402
  async function typegen(root, flags) {
package/dist/config.d.ts CHANGED
@@ -40,6 +40,10 @@ interface FutureConfig {
40
40
  unstable_optimizeDeps: boolean;
41
41
  unstable_subResourceIntegrity: boolean;
42
42
  unstable_trailingSlashAwareDataRequests: boolean;
43
+ /**
44
+ * Prerender with Vite Preview server
45
+ */
46
+ unstable_previewServerPrerendering?: boolean;
43
47
  /**
44
48
  * Enable route middleware
45
49
  */
@@ -148,6 +152,47 @@ type ReactRouterConfig = {
148
152
  * SPA without server-rendering. Default's to `true`.
149
153
  */
150
154
  ssr?: boolean;
155
+ /**
156
+ * An array of allowed origin hosts for action submissions to UI routes (does not apply
157
+ * to resource routes). Supports micromatch glob patterns (`*` to match one segment,
158
+ * `**` to match multiple).
159
+ *
160
+ * ```tsx
161
+ * export default {
162
+ * allowedActionOrigins: [
163
+ * "example.com",
164
+ * "*.example.com", // sub.example.com
165
+ * "**.example.com", // sub.domain.example.com
166
+ * ],
167
+ * } satisfies Config;
168
+ * ```
169
+ *
170
+ * If you need to set this value at runtime, you can do in by setting the value
171
+ * on the server build in your custom server. For example, when using `express`:
172
+ *
173
+ * ```ts
174
+ * import express from "express";
175
+ * import { createRequestHandler } from "@react-router/express";
176
+ * import type { ServerBuild } from "react-router";
177
+ *
178
+ * export const app = express();
179
+ *
180
+ * async function getBuild() {
181
+ * let build: ServerBuild = await import(
182
+ * "virtual:react-router/server-build"
183
+ * );
184
+ * return {
185
+ * ...build,
186
+ * allowedActionOrigins:
187
+ * process.env.NODE_ENV === "development"
188
+ * ? undefined
189
+ * : ["staging.example.com", "www.example.com"],
190
+ * };
191
+ * }
192
+ *
193
+ * app.use(createRequestHandler({ build: getBuild }));
194
+ */
195
+ allowedActionOrigins?: string[];
151
196
  };
152
197
  type ResolvedReactRouterConfig = Readonly<{
153
198
  /**
@@ -213,6 +258,11 @@ type ResolvedReactRouterConfig = Readonly<{
213
258
  * SPA without server-rendering. Default's to `true`.
214
259
  */
215
260
  ssr: boolean;
261
+ /**
262
+ * The allowed origins for actions / mutations. Does not apply to routes
263
+ * without a component. micromatch glob patterns are supported.
264
+ */
265
+ allowedActionOrigins: string[] | false;
216
266
  /**
217
267
  * The resolved array of route config entries exported from `routes.ts`
218
268
  */
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-f887449bb
2
+ * @react-router/dev v0.0.0-experimental-e067e5e52
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/routes.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-f887449bb
2
+ * @react-router/dev v0.0.0-experimental-e067e5e52
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-f887449bb
2
+ * @react-router/dev v0.0.0-experimental-e067e5e52
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -45,12 +45,8 @@ __export(cloudflare_exports, {
45
45
  module.exports = __toCommonJS(cloudflare_exports);
46
46
 
47
47
  // vite/cloudflare-dev-proxy.ts
48
- var import_node_fetch_server2 = require("@remix-run/node-fetch-server");
49
48
  var import_react_router = require("react-router");
50
49
 
51
- // vite/node-adapter.ts
52
- var import_node_fetch_server = require("@remix-run/node-fetch-server");
53
-
54
50
  // invariant.ts
55
51
  function invariant(value, message) {
56
52
  if (value === false || value === null || typeof value === "undefined") {
@@ -62,13 +58,14 @@ function invariant(value, message) {
62
58
  }
63
59
 
64
60
  // vite/node-adapter.ts
65
- function fromNodeRequest(nodeReq, nodeRes) {
61
+ async function fromNodeRequest(nodeReq, nodeRes) {
66
62
  invariant(
67
63
  nodeReq.originalUrl,
68
64
  "Expected `nodeReq.originalUrl` to be defined"
69
65
  );
70
66
  nodeReq.url = nodeReq.originalUrl;
71
- return (0, import_node_fetch_server.createRequest)(nodeReq, nodeRes);
67
+ const { createRequest } = await import("@remix-run/node-fetch-server");
68
+ return createRequest(nodeReq, nodeRes);
72
69
  }
73
70
 
74
71
  // vite/vite.ts
@@ -536,10 +533,12 @@ async function resolveConfig({
536
533
  unstable_optimizeDeps: userAndPresetConfigs.future?.unstable_optimizeDeps ?? false,
537
534
  unstable_subResourceIntegrity: userAndPresetConfigs.future?.unstable_subResourceIntegrity ?? false,
538
535
  unstable_trailingSlashAwareDataRequests: userAndPresetConfigs.future?.unstable_trailingSlashAwareDataRequests ?? false,
536
+ unstable_previewServerPrerendering: userAndPresetConfigs.future?.unstable_previewServerPrerendering ?? false,
539
537
  v8_middleware: userAndPresetConfigs.future?.v8_middleware ?? false,
540
538
  v8_splitRouteModules: userAndPresetConfigs.future?.v8_splitRouteModules ?? false,
541
539
  v8_viteEnvironmentApi: userAndPresetConfigs.future?.v8_viteEnvironmentApi ?? false
542
540
  };
541
+ let allowedActionOrigins = userAndPresetConfigs.allowedActionOrigins ?? false;
543
542
  let reactRouterConfig = deepFreeze({
544
543
  appDirectory,
545
544
  basename,
@@ -553,6 +552,7 @@ async function resolveConfig({
553
552
  serverBundles,
554
553
  serverModuleFormat,
555
554
  ssr,
555
+ allowedActionOrigins,
556
556
  unstable_routeConfig: routeConfig
557
557
  });
558
558
  for (let preset of reactRouterUserConfig.presets ?? []) {
@@ -818,6 +818,7 @@ var cloudflareDevProxyVitePlugin = (options = {}) => {
818
818
  }
819
819
  },
820
820
  configureServer: async (viteDevServer) => {
821
+ const { sendResponse } = await import("@remix-run/node-fetch-server");
821
822
  let context;
822
823
  let getContext = async () => {
823
824
  let { getPlatformProxy } = await importWrangler();
@@ -834,11 +835,11 @@ var cloudflareDevProxyVitePlugin = (options = {}) => {
834
835
  serverBuildId
835
836
  );
836
837
  let handler = (0, import_react_router.createRequestHandler)(build, "development");
837
- let req = fromNodeRequest(nodeReq, nodeRes);
838
+ let req = await fromNodeRequest(nodeReq, nodeRes);
838
839
  context ??= await getContext();
839
840
  let loadContext = getLoadContext ? await getLoadContext({ request: req, context }) : context;
840
841
  let res = await handler(req, loadContext);
841
- await (0, import_node_fetch_server2.sendResponse)(nodeRes, res);
842
+ await sendResponse(nodeRes, res);
842
843
  } catch (error) {
843
844
  next(error);
844
845
  }