@react-router/dev 0.0.0-experimental-fde52e515 → 0.0.0-experimental-a2c4d7fad

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @react-router/dev v0.0.0-experimental-fde52e515
3
+ * @react-router/dev v0.0.0-experimental-a2c4d7fad
4
4
  *
5
5
  * Copyright (c) Remix Software Inc.
6
6
  *
@@ -635,6 +635,25 @@ var init_profiler = __esm({
635
635
  }
636
636
  });
637
637
 
638
+ // vite/babel.ts
639
+ var babel_exports = {};
640
+ __export(babel_exports, {
641
+ generate: () => generate,
642
+ parse: () => import_parser.parse,
643
+ t: () => t,
644
+ traverse: () => traverse
645
+ });
646
+ var import_parser, t, traverse, generate;
647
+ var init_babel = __esm({
648
+ "vite/babel.ts"() {
649
+ "use strict";
650
+ import_parser = require("@babel/parser");
651
+ t = __toESM(require("@babel/types"));
652
+ traverse = require("@babel/traverse").default;
653
+ generate = require("@babel/generator").default;
654
+ }
655
+ });
656
+
638
657
  // typegen/paths.ts
639
658
  function getTypesDir(ctx) {
640
659
  return Path2.join(ctx.rootDirectory, ".react-router/types");
@@ -657,9 +676,8 @@ var init_paths = __esm({
657
676
  });
658
677
 
659
678
  // typegen/generate.ts
660
- function generate(ctx, route) {
679
+ function generate2(ctx, route) {
661
680
  const lineage = getRouteLineage(ctx.config.routes, route);
662
- const urlpath = lineage.map((route2) => route2.path).join("/");
663
681
  const typesPath = getTypesPath(ctx, route);
664
682
  const parents = lineage.slice(0, -1);
665
683
  const parentTypeImports = parents.map((parent, i) => {
@@ -677,22 +695,15 @@ function generate(ctx, route) {
677
695
  // ${route.file}
678
696
 
679
697
  import type * as T from "react-router/route-module"
698
+ import type { Routes } from "react-router/types"
680
699
 
681
700
  ${parentTypeImports}
682
701
 
683
- type Module = typeof import("../${Pathe2.filename(route.file)}.js")
702
+ type RouteId = "${route.id}"
684
703
 
685
- export type Info = {
704
+ export type Info = Routes[RouteId] & {
686
705
  parents: [${parents.map((_, i) => `Parent${i}`).join(", ")}],
687
- id: "${route.id}"
688
- file: "${route.file}"
689
- path: "${route.path}"
690
- params: {${formatParamProperties(
691
- urlpath
692
- )}} & { [key: string]: string | undefined }
693
- module: Module
694
- loaderData: T.CreateLoaderData<Module>
695
- actionData: T.CreateActionData<Module>
706
+ id: RouteId
696
707
  }
697
708
 
698
709
  export namespace Route {
@@ -727,36 +738,6 @@ function getRouteLineage(routes2, route) {
727
738
  result.reverse();
728
739
  return result;
729
740
  }
730
- function formatParamProperties(urlpath) {
731
- const params = parseParams(urlpath);
732
- const properties = Object.entries(params).map(([name, values]) => {
733
- if (values.length === 1) {
734
- const isOptional = values[0];
735
- return isOptional ? `"${name}"?: string` : `"${name}": string`;
736
- }
737
- const items = values.map(
738
- (isOptional) => isOptional ? "string | undefined" : "string"
739
- );
740
- return `"${name}": [${items.join(", ")}]`;
741
- });
742
- return properties.join("; ");
743
- }
744
- function parseParams(urlpath) {
745
- const result = {};
746
- let segments = urlpath.split("/");
747
- segments.forEach((segment) => {
748
- const match = segment.match(/^:([\w-]+)(\?)?/);
749
- if (!match) return;
750
- const param = match[1];
751
- const isOptional = match[2] !== void 0;
752
- result[param] ??= [];
753
- result[param].push(isOptional);
754
- return;
755
- });
756
- const hasSplat = segments.at(-1) === "*";
757
- if (hasSplat) result["*"] = [false];
758
- return result;
759
- }
760
741
  var import_dedent, Path3, Pathe2, noExtension;
761
742
  var init_generate = __esm({
762
743
  "typegen/generate.ts"() {
@@ -817,33 +798,110 @@ async function writeAll(ctx) {
817
798
  import_node_fs3.default.rmSync(typegenDir, { recursive: true, force: true });
818
799
  Object.values(ctx.config.routes).forEach((route) => {
819
800
  const typesPath = getTypesPath(ctx, route);
820
- const content = generate(ctx, route);
801
+ const content = generate2(ctx, route);
821
802
  import_node_fs3.default.mkdirSync(Path4.dirname(typesPath), { recursive: true });
822
803
  import_node_fs3.default.writeFileSync(typesPath, content);
823
804
  });
805
+ Object.values(ctx.config.routes).map(
806
+ (route) => t2.tsPropertySignature(
807
+ t2.stringLiteral(route.id),
808
+ t2.tsTypeAnnotation(
809
+ t2.tsTypeLiteral([
810
+ t2.tsPropertySignature(
811
+ t2.identifier("parentId"),
812
+ t2.tsTypeAnnotation(
813
+ route.parentId ? t2.tsLiteralType(t2.stringLiteral(route.parentId)) : t2.tsUndefinedKeyword()
814
+ )
815
+ ),
816
+ t2.tsPropertySignature(
817
+ t2.identifier("path"),
818
+ t2.tsTypeAnnotation(
819
+ route.path ? t2.tsLiteralType(t2.stringLiteral(route.path)) : t2.tsUndefinedKeyword()
820
+ )
821
+ ),
822
+ t2.tsPropertySignature(
823
+ t2.identifier("module"),
824
+ t2.tsTypeAnnotation(
825
+ t2.tsTypeQuery(t2.tsImportType(t2.stringLiteral(route.file)))
826
+ )
827
+ )
828
+ ])
829
+ )
830
+ )
831
+ );
832
+ const registerPath = Path4.join(typegenDir, "+register.ts");
833
+ import_node_fs3.default.writeFileSync(registerPath, register(ctx));
834
+ }
835
+ function register(ctx) {
836
+ const routes2 = generate(
837
+ t2.tsTypeAliasDeclaration(
838
+ t2.identifier("Routes"),
839
+ null,
840
+ t2.tsTypeLiteral(
841
+ Object.values(ctx.config.routes).map(
842
+ (route) => t2.tsPropertySignature(
843
+ t2.stringLiteral(route.id),
844
+ t2.tsTypeAnnotation(
845
+ t2.tsTypeLiteral([
846
+ t2.tsPropertySignature(
847
+ t2.identifier("parentId"),
848
+ t2.tsTypeAnnotation(
849
+ route.parentId ? t2.tsLiteralType(t2.stringLiteral(route.parentId)) : t2.tsUndefinedKeyword()
850
+ )
851
+ ),
852
+ t2.tsPropertySignature(
853
+ t2.identifier("path"),
854
+ t2.tsTypeAnnotation(
855
+ route.path ? t2.tsLiteralType(t2.stringLiteral(route.path)) : t2.tsUndefinedKeyword()
856
+ )
857
+ ),
858
+ t2.tsPropertySignature(
859
+ t2.identifier("module"),
860
+ t2.tsTypeAnnotation(
861
+ t2.tsTypeQuery(
862
+ t2.tsImportType(
863
+ t2.stringLiteral(compiledModulePath(ctx, route))
864
+ )
865
+ )
866
+ )
867
+ )
868
+ ])
869
+ )
870
+ )
871
+ )
872
+ )
873
+ )
874
+ ).code;
875
+ const registerTypes = import_dedent2.default`
876
+ import "react-router/types";
877
+
878
+ declare module "react-router/types" {
879
+ interface Register {
880
+ routes: Routes;
881
+ }
882
+ }
883
+ `;
884
+ return [registerTypes, routes2].join("\n\n");
885
+ }
886
+ function compiledModulePath(ctx, route) {
887
+ return "./" + Path4.relative(
888
+ ctx.rootDirectory,
889
+ Path4.join(ctx.config.appDirectory, route.file)
890
+ ).replace(/\.(js|ts)x?$/, ".js");
824
891
  }
825
- var import_node_fs3, Path4, import_picocolors3;
892
+ var import_node_fs3, import_dedent2, Path4, import_picocolors3, t2;
826
893
  var init_typegen = __esm({
827
894
  "typegen/index.ts"() {
828
895
  "use strict";
829
896
  import_node_fs3 = __toESM(require("fs"));
897
+ import_dedent2 = __toESM(require("dedent"));
830
898
  Path4 = __toESM(require("pathe"));
831
899
  import_picocolors3 = __toESM(require("picocolors"));
832
900
  init_config();
901
+ init_babel();
833
902
  init_generate();
834
903
  init_paths();
835
- }
836
- });
837
-
838
- // vite/babel.ts
839
- var import_parser, t, traverse, generate2;
840
- var init_babel = __esm({
841
- "vite/babel.ts"() {
842
- "use strict";
843
- import_parser = require("@babel/parser");
844
- t = __toESM(require("@babel/types"));
845
- traverse = require("@babel/traverse").default;
846
- generate2 = require("@babel/generator").default;
904
+ ({ t: t2 } = babel_exports);
847
905
  }
848
906
  });
849
907
 
@@ -970,11 +1028,11 @@ var init_route_chunks = __esm({
970
1028
  });
971
1029
 
972
1030
  // vite/with-props.ts
973
- var import_dedent2, vmod;
1031
+ var import_dedent3, vmod;
974
1032
  var init_with_props = __esm({
975
1033
  "vite/with-props.ts"() {
976
1034
  "use strict";
977
- import_dedent2 = __toESM(require("dedent"));
1035
+ import_dedent3 = __toESM(require("dedent"));
978
1036
  init_babel();
979
1037
  init_virtual_module();
980
1038
  vmod = create("with-props");
@@ -1577,8 +1635,8 @@ var babel = __toESM(require("@babel/core"));
1577
1635
  var import_plugin_syntax_jsx = __toESM(require("@babel/plugin-syntax-jsx"));
1578
1636
  var import_preset_typescript = __toESM(require("@babel/preset-typescript"));
1579
1637
  var import_prettier = __toESM(require("prettier"));
1580
- function transpile(tsx, options = {}) {
1581
- let mjs = babel.transformSync(tsx, {
1638
+ function transpile(tsx2, options = {}) {
1639
+ let mjs = babel.transformSync(tsx2, {
1582
1640
  compact: false,
1583
1641
  cwd: options.cwd,
1584
1642
  filename: options.filename,
@@ -1702,8 +1760,8 @@ async function checkForEntry(rootDirectory, appDirectory, entries2) {
1702
1760
  let entryPath = path9.resolve(appDirectory, entry);
1703
1761
  let exists = await import_fs_extra2.default.pathExists(entryPath);
1704
1762
  if (exists) {
1705
- let relative8 = path9.relative(rootDirectory, entryPath);
1706
- console.error(import_picocolors7.default.red(`Entry file ${relative8} already exists.`));
1763
+ let relative9 = path9.relative(rootDirectory, entryPath);
1764
+ console.error(import_picocolors7.default.red(`Entry file ${relative9} already exists.`));
1707
1765
  return process.exit(1);
1708
1766
  }
1709
1767
  }
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-fde52e515
2
+ * @react-router/dev v0.0.0-experimental-a2c4d7fad
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-fde52e515
2
+ * @react-router/dev v0.0.0-experimental-a2c4d7fad
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-fde52e515
2
+ * @react-router/dev v0.0.0-experimental-a2c4d7fad
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
package/dist/vite.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v0.0.0-experimental-fde52e515
2
+ * @react-router/dev v0.0.0-experimental-a2c4d7fad
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -59,6 +59,7 @@ var import_kebabCase = __toESM(require("lodash/kebabCase"));
59
59
 
60
60
  // typegen/index.ts
61
61
  var import_node_fs2 = __toESM(require("fs"));
62
+ var import_dedent2 = __toESM(require("dedent"));
62
63
  var Path4 = __toESM(require("pathe"));
63
64
  var import_picocolors2 = __toESM(require("picocolors"));
64
65
 
@@ -632,6 +633,19 @@ function findEntry(dir, basename2, options) {
632
633
  return void 0;
633
634
  }
634
635
 
636
+ // vite/babel.ts
637
+ var babel_exports = {};
638
+ __export(babel_exports, {
639
+ generate: () => generate,
640
+ parse: () => import_parser.parse,
641
+ t: () => t,
642
+ traverse: () => traverse
643
+ });
644
+ var import_parser = require("@babel/parser");
645
+ var t = __toESM(require("@babel/types"));
646
+ var traverse = require("@babel/traverse").default;
647
+ var generate = require("@babel/generator").default;
648
+
635
649
  // typegen/generate.ts
636
650
  var import_dedent = __toESM(require("dedent"));
637
651
  var Path3 = __toESM(require("pathe"));
@@ -653,9 +667,8 @@ function getTypesPath(ctx, route) {
653
667
  }
654
668
 
655
669
  // typegen/generate.ts
656
- function generate(ctx, route) {
670
+ function generate2(ctx, route) {
657
671
  const lineage = getRouteLineage(ctx.config.routes, route);
658
- const urlpath = lineage.map((route2) => route2.path).join("/");
659
672
  const typesPath = getTypesPath(ctx, route);
660
673
  const parents = lineage.slice(0, -1);
661
674
  const parentTypeImports = parents.map((parent, i) => {
@@ -673,22 +686,15 @@ function generate(ctx, route) {
673
686
  // ${route.file}
674
687
 
675
688
  import type * as T from "react-router/route-module"
689
+ import type { Routes } from "react-router/types"
676
690
 
677
691
  ${parentTypeImports}
678
692
 
679
- type Module = typeof import("../${Pathe2.filename(route.file)}.js")
693
+ type RouteId = "${route.id}"
680
694
 
681
- export type Info = {
695
+ export type Info = Routes[RouteId] & {
682
696
  parents: [${parents.map((_, i) => `Parent${i}`).join(", ")}],
683
- id: "${route.id}"
684
- file: "${route.file}"
685
- path: "${route.path}"
686
- params: {${formatParamProperties(
687
- urlpath
688
- )}} & { [key: string]: string | undefined }
689
- module: Module
690
- loaderData: T.CreateLoaderData<Module>
691
- actionData: T.CreateActionData<Module>
697
+ id: RouteId
692
698
  }
693
699
 
694
700
  export namespace Route {
@@ -724,38 +730,9 @@ function getRouteLineage(routes, route) {
724
730
  result.reverse();
725
731
  return result;
726
732
  }
727
- function formatParamProperties(urlpath) {
728
- const params = parseParams(urlpath);
729
- const properties = Object.entries(params).map(([name, values]) => {
730
- if (values.length === 1) {
731
- const isOptional = values[0];
732
- return isOptional ? `"${name}"?: string` : `"${name}": string`;
733
- }
734
- const items = values.map(
735
- (isOptional) => isOptional ? "string | undefined" : "string"
736
- );
737
- return `"${name}": [${items.join(", ")}]`;
738
- });
739
- return properties.join("; ");
740
- }
741
- function parseParams(urlpath) {
742
- const result = {};
743
- let segments = urlpath.split("/");
744
- segments.forEach((segment) => {
745
- const match = segment.match(/^:([\w-]+)(\?)?/);
746
- if (!match) return;
747
- const param = match[1];
748
- const isOptional = match[2] !== void 0;
749
- result[param] ??= [];
750
- result[param].push(isOptional);
751
- return;
752
- });
753
- const hasSplat = segments.at(-1) === "*";
754
- if (hasSplat) result["*"] = [false];
755
- return result;
756
- }
757
733
 
758
734
  // typegen/index.ts
735
+ var { t: t2 } = babel_exports;
759
736
  async function watch(rootDirectory, { logger } = {}) {
760
737
  const ctx = await createContext2({ rootDirectory, watch: true });
761
738
  await writeAll(ctx);
@@ -799,17 +776,97 @@ async function writeAll(ctx) {
799
776
  import_node_fs2.default.rmSync(typegenDir, { recursive: true, force: true });
800
777
  Object.values(ctx.config.routes).forEach((route) => {
801
778
  const typesPath = getTypesPath(ctx, route);
802
- const content = generate(ctx, route);
779
+ const content = generate2(ctx, route);
803
780
  import_node_fs2.default.mkdirSync(Path4.dirname(typesPath), { recursive: true });
804
781
  import_node_fs2.default.writeFileSync(typesPath, content);
805
782
  });
806
- }
783
+ Object.values(ctx.config.routes).map(
784
+ (route) => t2.tsPropertySignature(
785
+ t2.stringLiteral(route.id),
786
+ t2.tsTypeAnnotation(
787
+ t2.tsTypeLiteral([
788
+ t2.tsPropertySignature(
789
+ t2.identifier("parentId"),
790
+ t2.tsTypeAnnotation(
791
+ route.parentId ? t2.tsLiteralType(t2.stringLiteral(route.parentId)) : t2.tsUndefinedKeyword()
792
+ )
793
+ ),
794
+ t2.tsPropertySignature(
795
+ t2.identifier("path"),
796
+ t2.tsTypeAnnotation(
797
+ route.path ? t2.tsLiteralType(t2.stringLiteral(route.path)) : t2.tsUndefinedKeyword()
798
+ )
799
+ ),
800
+ t2.tsPropertySignature(
801
+ t2.identifier("module"),
802
+ t2.tsTypeAnnotation(
803
+ t2.tsTypeQuery(t2.tsImportType(t2.stringLiteral(route.file)))
804
+ )
805
+ )
806
+ ])
807
+ )
808
+ )
809
+ );
810
+ const registerPath = Path4.join(typegenDir, "+register.ts");
811
+ import_node_fs2.default.writeFileSync(registerPath, register(ctx));
812
+ }
813
+ function register(ctx) {
814
+ const routes = generate(
815
+ t2.tsTypeAliasDeclaration(
816
+ t2.identifier("Routes"),
817
+ null,
818
+ t2.tsTypeLiteral(
819
+ Object.values(ctx.config.routes).map(
820
+ (route) => t2.tsPropertySignature(
821
+ t2.stringLiteral(route.id),
822
+ t2.tsTypeAnnotation(
823
+ t2.tsTypeLiteral([
824
+ t2.tsPropertySignature(
825
+ t2.identifier("parentId"),
826
+ t2.tsTypeAnnotation(
827
+ route.parentId ? t2.tsLiteralType(t2.stringLiteral(route.parentId)) : t2.tsUndefinedKeyword()
828
+ )
829
+ ),
830
+ t2.tsPropertySignature(
831
+ t2.identifier("path"),
832
+ t2.tsTypeAnnotation(
833
+ route.path ? t2.tsLiteralType(t2.stringLiteral(route.path)) : t2.tsUndefinedKeyword()
834
+ )
835
+ ),
836
+ t2.tsPropertySignature(
837
+ t2.identifier("module"),
838
+ t2.tsTypeAnnotation(
839
+ t2.tsTypeQuery(
840
+ t2.tsImportType(
841
+ t2.stringLiteral(compiledModulePath(ctx, route))
842
+ )
843
+ )
844
+ )
845
+ )
846
+ ])
847
+ )
848
+ )
849
+ )
850
+ )
851
+ )
852
+ ).code;
853
+ const registerTypes = import_dedent2.default`
854
+ import "react-router/types";
807
855
 
808
- // vite/babel.ts
809
- var import_parser = require("@babel/parser");
810
- var t = __toESM(require("@babel/types"));
811
- var traverse = require("@babel/traverse").default;
812
- var generate2 = require("@babel/generator").default;
856
+ declare module "react-router/types" {
857
+ interface Register {
858
+ routes: Routes;
859
+ }
860
+ }
861
+ `;
862
+ return [registerTypes, routes].join("\n\n");
863
+ }
864
+ function compiledModulePath(ctx, route) {
865
+ return "./" + Path4.relative(
866
+ ctx.rootDirectory,
867
+ Path4.join(ctx.config.appDirectory, route.file)
868
+ ).replace(/\.(js|ts)x?$/, ".js");
869
+ }
813
870
 
814
871
  // vite/node-adapter.ts
815
872
  var import_node_events = require("events");
@@ -1597,7 +1654,7 @@ function getChunkedExport(code, exportName, generateOptions = {}, cache, cacheKe
1597
1654
  }
1598
1655
  throw new Error(`Unknown export node type: ${node.type}`);
1599
1656
  }).filter((node) => node !== null);
1600
- return generate2(ast, generateOptions);
1657
+ return generate(ast, generateOptions);
1601
1658
  }
1602
1659
  );
1603
1660
  }
@@ -1713,7 +1770,7 @@ function omitChunkedExports(code, exportNames, generateOptions = {}, cache, cach
1713
1770
  if (ast.program.body.length === 0) {
1714
1771
  return void 0;
1715
1772
  }
1716
- return generate2(ast, generateOptions);
1773
+ return generate(ast, generateOptions);
1717
1774
  }
1718
1775
  );
1719
1776
  }
@@ -1775,7 +1832,7 @@ function getRouteChunkNameFromModuleId(id) {
1775
1832
  }
1776
1833
 
1777
1834
  // vite/with-props.ts
1778
- var import_dedent2 = __toESM(require("dedent"));
1835
+ var import_dedent3 = __toESM(require("dedent"));
1779
1836
  var vmod = create("with-props");
1780
1837
  var NAMED_COMPONENT_EXPORTS = ["HydrateFallback", "ErrorBoundary"];
1781
1838
  var plugin = {
@@ -1786,7 +1843,7 @@ var plugin = {
1786
1843
  },
1787
1844
  async load(id) {
1788
1845
  if (id !== vmod.resolvedId) return;
1789
- return import_dedent2.default`
1846
+ return import_dedent3.default`
1790
1847
  import { createElement as h } from "react";
1791
1848
  import { useActionData, useLoaderData, useMatches, useParams, useRouteError } from "react-router";
1792
1849
 
@@ -1806,8 +1863,6 @@ var plugin = {
1806
1863
  return function Wrapped() {
1807
1864
  const props = {
1808
1865
  params: useParams(),
1809
- loaderData: useLoaderData(),
1810
- actionData: useActionData(),
1811
1866
  };
1812
1867
  return h(HydrateFallback, props);
1813
1868
  };
@@ -2168,10 +2223,6 @@ var reactRouterVitePlugin = () => {
2168
2223
  // Otherwise, all routes are imported as usual
2169
2224
  ctx.reactRouterConfig.routes
2170
2225
  );
2171
- let prerenderPaths = await getPrerenderPaths(
2172
- ctx.reactRouterConfig.prerender,
2173
- routes
2174
- );
2175
2226
  return `
2176
2227
  import * as entryServer from ${JSON.stringify(
2177
2228
  resolveFileUrl(ctx, ctx.entryServerFilePath)
@@ -2198,7 +2249,6 @@ var reactRouterVitePlugin = () => {
2198
2249
  export const future = ${JSON.stringify(ctx.reactRouterConfig.future)};
2199
2250
  export const ssr = ${ctx.reactRouterConfig.ssr};
2200
2251
  export const isSpaMode = ${isSpaModeEnabled(ctx.reactRouterConfig)};
2201
- export const prerender = ${JSON.stringify(prerenderPaths)};
2202
2252
  export const publicPath = ${JSON.stringify(ctx.publicPath)};
2203
2253
  export const entry = { module: entryServer };
2204
2254
  export const routes = {
@@ -2725,7 +2775,7 @@ var reactRouterVitePlugin = () => {
2725
2775
  ].join("\n")
2726
2776
  );
2727
2777
  }
2728
- if (isPrerenderingEnabled(ctx.reactRouterConfig)) {
2778
+ if (ctx.reactRouterConfig.prerender != null && ctx.reactRouterConfig.prerender !== false) {
2729
2779
  await handlePrerender(
2730
2780
  viteConfig,
2731
2781
  ctx.reactRouterConfig,
@@ -2999,15 +3049,12 @@ var reactRouterVitePlugin = () => {
2999
3049
  if (!route) return;
3000
3050
  if (!options?.ssr && isSpaModeEnabled(ctx.reactRouterConfig)) {
3001
3051
  let exportNames = getExportNames(code);
3002
- let serverOnlyExports = exportNames.filter((exp) => {
3003
- if (route.id === "root" && exp === "loader") {
3004
- return false;
3005
- }
3006
- return SERVER_ONLY_ROUTE_EXPORTS.includes(exp);
3007
- });
3052
+ let serverOnlyExports = exportNames.filter(
3053
+ (exp) => SERVER_ONLY_ROUTE_EXPORTS.includes(exp)
3054
+ );
3008
3055
  if (serverOnlyExports.length > 0) {
3009
3056
  let str = serverOnlyExports.map((e) => `\`${e}\``).join(", ");
3010
- let message = `SPA Mode: ${serverOnlyExports.length} invalid route export(s) in \`${route.file}\`: ${str}. See https://reactrouter.com/how-to/spa for more information.`;
3057
+ let message = `SPA Mode: ${serverOnlyExports.length} invalid route export(s) in \`${route.file}\`: ${str}. See https://remix.run/guides/spa-mode for more information.`;
3011
3058
  throw Error(message);
3012
3059
  }
3013
3060
  if (route.id !== "root") {
@@ -3015,7 +3062,7 @@ var reactRouterVitePlugin = () => {
3015
3062
  (exp) => exp === "HydrateFallback"
3016
3063
  );
3017
3064
  if (hasHydrateFallback) {
3018
- let message = `SPA Mode: Invalid \`HydrateFallback\` export found in \`${route.file}\`. \`HydrateFallback\` is only permitted on the root route in SPA Mode. See https://reactrouter.com/how-to/spa for more information.`;
3065
+ let message = `SPA Mode: Invalid \`HydrateFallback\` export found in \`${route.file}\`. \`HydrateFallback\` is only permitted on the root route in SPA Mode. See https://remix.run/guides/spa-mode for more information.`;
3019
3066
  throw Error(message);
3020
3067
  }
3021
3068
  }
@@ -3026,7 +3073,7 @@ var reactRouterVitePlugin = () => {
3026
3073
  removeExports(ast, SERVER_ONLY_ROUTE_EXPORTS);
3027
3074
  }
3028
3075
  transform(ast);
3029
- return generate2(ast, {
3076
+ return generate(ast, {
3030
3077
  sourceMaps: true,
3031
3078
  filename: id,
3032
3079
  sourceFileName: filepath
@@ -3308,11 +3355,8 @@ async function getRouteMetadata(cache, ctx, viteChildCompiler, route, readRouteF
3308
3355
  };
3309
3356
  return info;
3310
3357
  }
3311
- function isPrerenderingEnabled(reactRouterConfig) {
3312
- return reactRouterConfig.prerender != null && reactRouterConfig.prerender !== false;
3313
- }
3314
3358
  function isSpaModeEnabled(reactRouterConfig) {
3315
- return reactRouterConfig.ssr === false && !isPrerenderingEnabled(reactRouterConfig);
3359
+ return reactRouterConfig.ssr === false && (reactRouterConfig.prerender == null || reactRouterConfig.prerender === false || Array.isArray(reactRouterConfig.prerender) && reactRouterConfig.prerender.length === 1 && reactRouterConfig.prerender[0] === "/");
3316
3360
  }
3317
3361
  async function getPrerenderBuildAndHandler(viteConfig, serverBuildDirectory, serverBuildFile) {
3318
3362
  let serverBuildPath = path6.join(serverBuildDirectory, serverBuildFile);
@@ -3324,44 +3368,20 @@ async function getPrerenderBuildAndHandler(viteConfig, serverBuildDirectory, ser
3324
3368
  };
3325
3369
  }
3326
3370
  async function handleSpaMode(viteConfig, reactRouterConfig, serverBuildDirectory, serverBuildFile, clientBuildDirectory) {
3327
- let { build, handler } = await getPrerenderBuildAndHandler(
3371
+ let { handler } = await getPrerenderBuildAndHandler(
3328
3372
  viteConfig,
3329
3373
  serverBuildDirectory,
3330
3374
  serverBuildFile
3331
3375
  );
3332
- let request = new Request(`http://localhost${reactRouterConfig.basename}`, {
3333
- headers: {
3334
- // Enable SPA mode in the server runtime and only render down to the root
3335
- "X-React-Router-SPA-Mode": "yes"
3336
- }
3337
- });
3376
+ let request = new Request(`http://localhost${reactRouterConfig.basename}`);
3338
3377
  let response = await handler(request);
3339
3378
  let html = await response.text();
3340
- let isPrerenderSpaFallback = build.prerender.includes("/");
3341
- let filename3 = isPrerenderSpaFallback ? "__spa-fallback__.html" : "index.html";
3342
- if (response.status !== 200) {
3343
- if (isPrerenderSpaFallback) {
3344
- throw new Error(
3345
- `Prerender: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering your \`${filename3}\` file.
3346
- ` + html
3347
- );
3348
- } else {
3349
- throw new Error(
3350
- `SPA Mode: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering your \`${filename3}\` file.
3351
- ` + html
3352
- );
3353
- }
3354
- }
3355
- if (!html.includes("window.__reactRouterContext =") || !html.includes("window.__reactRouterRouteModules =")) {
3356
- throw new Error(
3357
- "SPA Mode: Did you forget to include `<Scripts/>` in your root route? Your pre-rendered HTML cannot hydrate without `<Scripts />`."
3358
- );
3359
- }
3360
- await fse.writeFile(path6.join(clientBuildDirectory, filename3), html);
3361
- let prettyDir = path6.relative(process.cwd(), clientBuildDirectory);
3362
- let prettyPath = path6.join(prettyDir, filename3);
3363
- let prefix = isPrerenderSpaFallback ? "Prerender" : "SPA Mode";
3364
- viteConfig.logger.info(`${prefix}: Generated \`${prettyPath}\``);
3379
+ validatePrerenderedResponse(response, html, "SPA Mode", "/");
3380
+ validatePrerenderedHtml(html, "SPA Mode");
3381
+ await fse.writeFile(path6.join(clientBuildDirectory, "index.html"), html);
3382
+ viteConfig.logger.info(
3383
+ "SPA Mode: index.html has been written to your " + import_picocolors3.default.bold(path6.relative(process.cwd(), clientBuildDirectory)) + " directory"
3384
+ );
3365
3385
  }
3366
3386
  async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirectory, serverBuildPath, clientBuildDirectory) {
3367
3387
  let { build, handler } = await getPrerenderBuildAndHandler(
@@ -3370,22 +3390,29 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
3370
3390
  serverBuildPath
3371
3391
  );
3372
3392
  let routes = createPrerenderRoutes(build.routes);
3373
- let prerenderedRoutes = /* @__PURE__ */ new Set();
3393
+ let routesToPrerender;
3394
+ if (typeof reactRouterConfig.prerender === "boolean") {
3395
+ invariant(reactRouterConfig.prerender, "Expected prerender:true");
3396
+ routesToPrerender = determineStaticPrerenderRoutes(
3397
+ routes,
3398
+ viteConfig,
3399
+ true
3400
+ );
3401
+ } else if (typeof reactRouterConfig.prerender === "function") {
3402
+ routesToPrerender = await reactRouterConfig.prerender({
3403
+ getStaticPaths: () => determineStaticPrerenderRoutes(routes, viteConfig, false)
3404
+ });
3405
+ } else {
3406
+ routesToPrerender = reactRouterConfig.prerender || ["/"];
3407
+ }
3374
3408
  let headers = {
3375
3409
  // Header that can be used in the loader to know if you're running at
3376
3410
  // build time or runtime
3377
3411
  "X-React-Router-Prerender": "yes"
3378
3412
  };
3379
- for (let path7 of build.prerender) {
3413
+ for (let path7 of routesToPrerender) {
3380
3414
  let matches = (0, import_react_router2.matchRoutes)(routes, `/${path7}/`.replace(/^\/\/+/, "/"));
3381
- invariant(
3382
- matches,
3383
- `Unable to prerender path because it does not match any routes: ${path7}`
3384
- );
3385
- matches.forEach((m) => prerenderedRoutes.add(m.route.id));
3386
- let hasLoaders = matches.some(
3387
- (m) => build.assets.routes[m.route.id]?.hasLoader
3388
- );
3415
+ let hasLoaders = matches?.some((m) => m.route.loader);
3389
3416
  let data;
3390
3417
  if (hasLoaders) {
3391
3418
  data = await prerenderData(
@@ -3425,34 +3452,8 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
3425
3452
  );
3426
3453
  }
3427
3454
  }
3428
- if (reactRouterConfig.ssr === false) {
3429
- let errors = [];
3430
- for (let [routeId, route] of Object.entries(build.routes)) {
3431
- let invalidApis = [];
3432
- if (route) {
3433
- if (route.module.headers) invalidApis.push("headers");
3434
- if (route.module.action) invalidApis.push("action");
3435
- if (invalidApis.length > 0) {
3436
- errors.push(
3437
- `Prerender: ${invalidApis.length} invalid route export(s) in \`${route.id}\` when prerendering with \`ssr:false\`: ${invalidApis.join(", ")}. See https://reactrouter.com/how-to/spa for more information.`
3438
- );
3439
- }
3440
- if (route.module.loader && !prerenderedRoutes.has(routeId)) {
3441
- errors.push(
3442
- `Prerender: 1 invalid route export in \`${route.id}\` when using \`ssr:false\` with \`prerender\` because the route is never prerendered so the loader will never be called. See https://reactrouter.com/how-to/spa for more information.`
3443
- );
3444
- }
3445
- }
3446
- }
3447
- if (errors.length > 0) {
3448
- viteConfig.logger.error(errors.join("\n"));
3449
- throw new Error(
3450
- "Invalid route exports found when prerendering with `ssr:false`"
3451
- );
3452
- }
3453
- }
3454
3455
  }
3455
- function getStaticPrerenderPaths(routes) {
3456
+ function determineStaticPrerenderRoutes(routes, viteConfig, isBooleanUsage = false) {
3456
3457
  let paths = ["/"];
3457
3458
  let paramRoutes = [];
3458
3459
  function recurse(subtree, prefix = "") {
@@ -3472,29 +3473,28 @@ function getStaticPrerenderPaths(routes) {
3472
3473
  }
3473
3474
  }
3474
3475
  recurse(routes);
3475
- return {
3476
- paths: paths.map((p) => p.replace(/\/\/+/g, "/").replace(/(.+)\/$/, "$1")),
3477
- paramRoutes
3478
- };
3476
+ if (isBooleanUsage && paramRoutes.length > 0) {
3477
+ viteConfig.logger.warn(
3478
+ [
3479
+ "\u26A0\uFE0F Paths with dynamic/splat params cannot be prerendered when using `prerender: true`.",
3480
+ "You may want to use the `prerender()` API to prerender the following paths:",
3481
+ ...paramRoutes.map((p) => " - " + p)
3482
+ ].join("\n")
3483
+ );
3484
+ }
3485
+ return paths.map((p) => p.replace(/\/\/+/g, "/").replace(/(.+)\/$/, "$1"));
3479
3486
  }
3480
3487
  async function prerenderData(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
3481
3488
  let normalizedPath = `${reactRouterConfig.basename}${prerenderPath === "/" ? "/_root.data" : `${prerenderPath.replace(/\/$/, "")}.data`}`.replace(/\/\/+/g, "/");
3482
3489
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
3483
3490
  let response = await handler(request);
3484
3491
  let data = await response.text();
3485
- if (response.status !== 200) {
3486
- throw new Error(
3487
- `Prerender: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${path6}\` path.
3488
- ${normalizedPath}`
3489
- );
3490
- }
3492
+ validatePrerenderedResponse(response, data, "Prerender", normalizedPath);
3491
3493
  let outdir = path6.relative(process.cwd(), clientBuildDirectory);
3492
3494
  let outfile = path6.join(outdir, ...normalizedPath.split("/"));
3493
3495
  await fse.ensureDir(path6.dirname(outfile));
3494
3496
  await fse.outputFile(outfile, data);
3495
- viteConfig.logger.info(
3496
- `Prerender Data: ${prerenderPath} -> ${import_picocolors3.default.bold(outfile)}`
3497
- );
3497
+ viteConfig.logger.info(`Prerender: Generated ${import_picocolors3.default.bold(outfile)}`);
3498
3498
  return data;
3499
3499
  }
3500
3500
  async function prerenderRoute(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
@@ -3505,83 +3505,42 @@ async function prerenderRoute(handler, prerenderPath, clientBuildDirectory, reac
3505
3505
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
3506
3506
  let response = await handler(request);
3507
3507
  let html = await response.text();
3508
- if (response.status !== 200) {
3509
- throw new Error(
3510
- `Prerender: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${normalizedPath}\` path.
3511
- ${html}`
3512
- );
3508
+ validatePrerenderedResponse(response, html, "Prerender", normalizedPath);
3509
+ if (!reactRouterConfig.ssr) {
3510
+ validatePrerenderedHtml(html, "Prerender");
3513
3511
  }
3514
3512
  let outdir = path6.relative(process.cwd(), clientBuildDirectory);
3515
3513
  let outfile = path6.join(outdir, ...normalizedPath.split("/"), "index.html");
3516
3514
  await fse.ensureDir(path6.dirname(outfile));
3517
3515
  await fse.outputFile(outfile, html);
3518
- viteConfig.logger.info(
3519
- `Prerender: ${prerenderPath} -> ${import_picocolors3.default.bold(outfile)}`
3520
- );
3516
+ viteConfig.logger.info(`Prerender: Generated ${import_picocolors3.default.bold(outfile)}`);
3521
3517
  }
3522
3518
  async function prerenderResourceRoute(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
3523
3519
  let normalizedPath = `${reactRouterConfig.basename}${prerenderPath}/`.replace(/\/\/+/g, "/").replace(/\/$/g, "");
3524
3520
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
3525
3521
  let response = await handler(request);
3526
3522
  let text = await response.text();
3527
- if (response.status !== 200) {
3528
- throw new Error(
3529
- `Prerender: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${normalizedPath}\` path.
3530
- ${text}`
3531
- );
3532
- }
3523
+ validatePrerenderedResponse(response, text, "Prerender", normalizedPath);
3533
3524
  let outdir = path6.relative(process.cwd(), clientBuildDirectory);
3534
3525
  let outfile = path6.join(outdir, ...normalizedPath.split("/"));
3535
3526
  await fse.ensureDir(path6.dirname(outfile));
3536
3527
  await fse.outputFile(outfile, text);
3537
- viteConfig.logger.info(
3538
- `Prerender: ${prerenderPath} -> ${import_picocolors3.default.bold(outfile)}`
3539
- );
3528
+ viteConfig.logger.info(`Prerender: Generated ${import_picocolors3.default.bold(outfile)}`);
3540
3529
  }
3541
- async function getPrerenderPaths(prerender, routes) {
3542
- let prerenderPaths = [];
3543
- if (prerender != null && prerender !== false) {
3544
- let prerenderRoutes = createPrerenderRoutes(routes);
3545
- if (prerender === true) {
3546
- let { paths, paramRoutes } = getStaticPrerenderPaths(prerenderRoutes);
3547
- if (paramRoutes.length > 0) {
3548
- console.warn(
3549
- import_picocolors3.default.yellow(
3550
- [
3551
- "\u26A0\uFE0F Paths with dynamic/splat params cannot be prerendered when using `prerender: true`. You may want to use the `prerender()` API to prerender the following paths:",
3552
- ...paramRoutes.map((p) => " - " + p)
3553
- ].join("\n")
3554
- )
3555
- );
3556
- }
3557
- prerenderPaths = paths;
3558
- } else if (typeof prerender === "function") {
3559
- prerenderPaths = await prerender({
3560
- getStaticPaths: () => getStaticPrerenderPaths(prerenderRoutes).paths
3561
- });
3562
- } else {
3563
- prerenderPaths = prerender || ["/"];
3564
- }
3530
+ function validatePrerenderedResponse(response, html, prefix, path7) {
3531
+ if (response.status !== 200) {
3532
+ throw new Error(
3533
+ `${prefix}: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${path7}\` path.
3534
+ ${html}`
3535
+ );
3565
3536
  }
3566
- return prerenderPaths;
3567
3537
  }
3568
- function createPrerenderRoutes(manifest, parentId = "", routesByParentId = groupRoutesByParentId2(manifest)) {
3569
- return (routesByParentId[parentId] || []).map((route) => {
3570
- let commonRoute = {
3571
- id: route.id,
3572
- path: route.path
3573
- };
3574
- if (route.index) {
3575
- return {
3576
- index: true,
3577
- ...commonRoute
3578
- };
3579
- }
3580
- return {
3581
- children: createPrerenderRoutes(manifest, route.id, routesByParentId),
3582
- ...commonRoute
3583
- };
3584
- });
3538
+ function validatePrerenderedHtml(html, prefix) {
3539
+ if (!html.includes("window.__reactRouterContext =") || !html.includes("window.__reactRouterRouteModules =")) {
3540
+ throw new Error(
3541
+ `${prefix}: Did you forget to include <Scripts/> in your root route? Your pre-rendered HTML files cannot hydrate without \`<Scripts />\`.`
3542
+ );
3543
+ }
3585
3544
  }
3586
3545
  function groupRoutesByParentId2(manifest) {
3587
3546
  let routes = {};
@@ -3596,6 +3555,27 @@ function groupRoutesByParentId2(manifest) {
3596
3555
  });
3597
3556
  return routes;
3598
3557
  }
3558
+ function createPrerenderRoutes(manifest, parentId = "", routesByParentId = groupRoutesByParentId2(manifest)) {
3559
+ return (routesByParentId[parentId] || []).map((route) => {
3560
+ let commonRoute = {
3561
+ // Always include root due to default boundaries
3562
+ hasErrorBoundary: route.id === "root" || route.module.ErrorBoundary != null,
3563
+ id: route.id,
3564
+ path: route.path,
3565
+ loader: route.module.loader ? () => null : void 0,
3566
+ action: void 0,
3567
+ handle: route.module.handle
3568
+ };
3569
+ return route.index ? {
3570
+ index: true,
3571
+ ...commonRoute
3572
+ } : {
3573
+ caseSensitive: route.caseSensitive,
3574
+ children: createPrerenderRoutes(manifest, route.id, routesByParentId),
3575
+ ...commonRoute
3576
+ };
3577
+ });
3578
+ }
3599
3579
  function getAddressableRoutes(routes) {
3600
3580
  let nonAddressableIds = /* @__PURE__ */ new Set();
3601
3581
  for (let id in routes) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-router/dev",
3
- "version": "0.0.0-experimental-fde52e515",
3
+ "version": "0.0.0-experimental-a2c4d7fad",
4
4
  "description": "Dev tools and CLI for React Router",
5
5
  "homepage": "https://reactrouter.com",
6
6
  "bugs": {
@@ -88,7 +88,7 @@
88
88
  "set-cookie-parser": "^2.6.0",
89
89
  "valibot": "^0.41.0",
90
90
  "vite-node": "3.0.0-beta.2",
91
- "@react-router/node": "0.0.0-experimental-fde52e515"
91
+ "@react-router/node": "0.0.0-experimental-a2c4d7fad"
92
92
  },
93
93
  "devDependencies": {
94
94
  "@types/babel__core": "^7.20.5",
@@ -117,15 +117,15 @@
117
117
  "vite": "^6.0.0",
118
118
  "wireit": "0.14.9",
119
119
  "wrangler": "^3.28.2",
120
- "react-router": "^0.0.0-experimental-fde52e515",
121
- "@react-router/serve": "0.0.0-experimental-fde52e515"
120
+ "react-router": "^0.0.0-experimental-a2c4d7fad",
121
+ "@react-router/serve": "0.0.0-experimental-a2c4d7fad"
122
122
  },
123
123
  "peerDependencies": {
124
124
  "typescript": "^5.1.0",
125
125
  "vite": "^5.1.0 || ^6.0.0",
126
126
  "wrangler": "^3.28.2",
127
- "@react-router/serve": "^0.0.0-experimental-fde52e515",
128
- "react-router": "^0.0.0-experimental-fde52e515"
127
+ "@react-router/serve": "^0.0.0-experimental-a2c4d7fad",
128
+ "react-router": "^0.0.0-experimental-a2c4d7fad"
129
129
  },
130
130
  "peerDependenciesMeta": {
131
131
  "@react-router/serve": {