@react-router/dev 0.0.0-experimental-b9cf540fe → 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-b9cf540fe
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-b9cf540fe
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-b9cf540fe
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-b9cf540fe
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-b9cf540fe
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,11 +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
- ctx.reactRouterConfig.ssr,
2174
- routes
2175
- );
2176
2226
  return `
2177
2227
  import * as entryServer from ${JSON.stringify(
2178
2228
  resolveFileUrl(ctx, ctx.entryServerFilePath)
@@ -2199,7 +2249,6 @@ var reactRouterVitePlugin = () => {
2199
2249
  export const future = ${JSON.stringify(ctx.reactRouterConfig.future)};
2200
2250
  export const ssr = ${ctx.reactRouterConfig.ssr};
2201
2251
  export const isSpaMode = ${isSpaModeEnabled(ctx.reactRouterConfig)};
2202
- export const prerender = ${JSON.stringify(prerenderPaths)};
2203
2252
  export const publicPath = ${JSON.stringify(ctx.publicPath)};
2204
2253
  export const entry = { module: entryServer };
2205
2254
  export const routes = {
@@ -2726,7 +2775,7 @@ var reactRouterVitePlugin = () => {
2726
2775
  ].join("\n")
2727
2776
  );
2728
2777
  }
2729
- if (isPrerenderingEnabled(ctx.reactRouterConfig)) {
2778
+ if (ctx.reactRouterConfig.prerender != null && ctx.reactRouterConfig.prerender !== false) {
2730
2779
  await handlePrerender(
2731
2780
  viteConfig,
2732
2781
  ctx.reactRouterConfig,
@@ -3000,15 +3049,12 @@ var reactRouterVitePlugin = () => {
3000
3049
  if (!route) return;
3001
3050
  if (!options?.ssr && isSpaModeEnabled(ctx.reactRouterConfig)) {
3002
3051
  let exportNames = getExportNames(code);
3003
- let serverOnlyExports = exportNames.filter((exp) => {
3004
- if (route.id === "root" && exp === "loader") {
3005
- return false;
3006
- }
3007
- return SERVER_ONLY_ROUTE_EXPORTS.includes(exp);
3008
- });
3052
+ let serverOnlyExports = exportNames.filter(
3053
+ (exp) => SERVER_ONLY_ROUTE_EXPORTS.includes(exp)
3054
+ );
3009
3055
  if (serverOnlyExports.length > 0) {
3010
3056
  let str = serverOnlyExports.map((e) => `\`${e}\``).join(", ");
3011
- 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.`;
3012
3058
  throw Error(message);
3013
3059
  }
3014
3060
  if (route.id !== "root") {
@@ -3016,7 +3062,7 @@ var reactRouterVitePlugin = () => {
3016
3062
  (exp) => exp === "HydrateFallback"
3017
3063
  );
3018
3064
  if (hasHydrateFallback) {
3019
- 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.`;
3020
3066
  throw Error(message);
3021
3067
  }
3022
3068
  }
@@ -3027,7 +3073,7 @@ var reactRouterVitePlugin = () => {
3027
3073
  removeExports(ast, SERVER_ONLY_ROUTE_EXPORTS);
3028
3074
  }
3029
3075
  transform(ast);
3030
- return generate2(ast, {
3076
+ return generate(ast, {
3031
3077
  sourceMaps: true,
3032
3078
  filename: id,
3033
3079
  sourceFileName: filepath
@@ -3309,11 +3355,8 @@ async function getRouteMetadata(cache, ctx, viteChildCompiler, route, readRouteF
3309
3355
  };
3310
3356
  return info;
3311
3357
  }
3312
- function isPrerenderingEnabled(reactRouterConfig) {
3313
- return reactRouterConfig.prerender != null && reactRouterConfig.prerender !== false;
3314
- }
3315
3358
  function isSpaModeEnabled(reactRouterConfig) {
3316
- 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] === "/");
3317
3360
  }
3318
3361
  async function getPrerenderBuildAndHandler(viteConfig, serverBuildDirectory, serverBuildFile) {
3319
3362
  let serverBuildPath = path6.join(serverBuildDirectory, serverBuildFile);
@@ -3325,44 +3368,20 @@ async function getPrerenderBuildAndHandler(viteConfig, serverBuildDirectory, ser
3325
3368
  };
3326
3369
  }
3327
3370
  async function handleSpaMode(viteConfig, reactRouterConfig, serverBuildDirectory, serverBuildFile, clientBuildDirectory) {
3328
- let { build, handler } = await getPrerenderBuildAndHandler(
3371
+ let { handler } = await getPrerenderBuildAndHandler(
3329
3372
  viteConfig,
3330
3373
  serverBuildDirectory,
3331
3374
  serverBuildFile
3332
3375
  );
3333
- let request = new Request(`http://localhost${reactRouterConfig.basename}`, {
3334
- headers: {
3335
- // Enable SPA mode in the server runtime and only render down to the root
3336
- "X-React-Router-SPA-Mode": "yes"
3337
- }
3338
- });
3376
+ let request = new Request(`http://localhost${reactRouterConfig.basename}`);
3339
3377
  let response = await handler(request);
3340
3378
  let html = await response.text();
3341
- let isPrerenderSpaFallback = build.prerender.includes("/");
3342
- let filename3 = isPrerenderSpaFallback ? "__spa-fallback__.html" : "index.html";
3343
- if (response.status !== 200) {
3344
- if (isPrerenderSpaFallback) {
3345
- throw new Error(
3346
- `Prerender: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering your \`${filename3}\` file.
3347
- ` + html
3348
- );
3349
- } else {
3350
- throw new Error(
3351
- `SPA Mode: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering your \`${filename3}\` file.
3352
- ` + html
3353
- );
3354
- }
3355
- }
3356
- if (!html.includes("window.__reactRouterContext =") || !html.includes("window.__reactRouterRouteModules =")) {
3357
- throw new Error(
3358
- "SPA Mode: Did you forget to include `<Scripts/>` in your root route? Your pre-rendered HTML cannot hydrate without `<Scripts />`."
3359
- );
3360
- }
3361
- await fse.writeFile(path6.join(clientBuildDirectory, filename3), html);
3362
- let prettyDir = path6.relative(process.cwd(), clientBuildDirectory);
3363
- let prettyPath = path6.join(prettyDir, filename3);
3364
- let prefix = isPrerenderSpaFallback ? "Prerender" : "SPA Mode";
3365
- 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
+ );
3366
3385
  }
3367
3386
  async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirectory, serverBuildPath, clientBuildDirectory) {
3368
3387
  let { build, handler } = await getPrerenderBuildAndHandler(
@@ -3371,22 +3390,29 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
3371
3390
  serverBuildPath
3372
3391
  );
3373
3392
  let routes = createPrerenderRoutes(build.routes);
3374
- 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
+ }
3375
3408
  let headers = {
3376
3409
  // Header that can be used in the loader to know if you're running at
3377
3410
  // build time or runtime
3378
3411
  "X-React-Router-Prerender": "yes"
3379
3412
  };
3380
- for (let path7 of build.prerender) {
3413
+ for (let path7 of routesToPrerender) {
3381
3414
  let matches = (0, import_react_router2.matchRoutes)(routes, `/${path7}/`.replace(/^\/\/+/, "/"));
3382
- invariant(
3383
- matches,
3384
- `Unable to prerender path because it does not match any routes: ${path7}`
3385
- );
3386
- matches.forEach((m) => prerenderedRoutes.add(m.route.id));
3387
- let hasLoaders = matches.some(
3388
- (m) => build.assets.routes[m.route.id]?.hasLoader
3389
- );
3415
+ let hasLoaders = matches?.some((m) => m.route.loader);
3390
3416
  let data;
3391
3417
  if (hasLoaders) {
3392
3418
  data = await prerenderData(
@@ -3426,34 +3452,8 @@ async function handlePrerender(viteConfig, reactRouterConfig, serverBuildDirecto
3426
3452
  );
3427
3453
  }
3428
3454
  }
3429
- if (reactRouterConfig.ssr === false) {
3430
- let errors = [];
3431
- for (let [routeId, route] of Object.entries(build.routes)) {
3432
- let invalidApis = [];
3433
- if (route) {
3434
- if (route.module.headers) invalidApis.push("headers");
3435
- if (route.module.action) invalidApis.push("action");
3436
- if (invalidApis.length > 0) {
3437
- errors.push(
3438
- `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.`
3439
- );
3440
- }
3441
- if (route.module.loader && !prerenderedRoutes.has(routeId)) {
3442
- errors.push(
3443
- `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.`
3444
- );
3445
- }
3446
- }
3447
- }
3448
- if (errors.length > 0) {
3449
- viteConfig.logger.error(errors.join("\n"));
3450
- throw new Error(
3451
- "Invalid route exports found when prerendering with `ssr:false`"
3452
- );
3453
- }
3454
- }
3455
3455
  }
3456
- function getStaticPrerenderPaths(routes) {
3456
+ function determineStaticPrerenderRoutes(routes, viteConfig, isBooleanUsage = false) {
3457
3457
  let paths = ["/"];
3458
3458
  let paramRoutes = [];
3459
3459
  function recurse(subtree, prefix = "") {
@@ -3473,29 +3473,28 @@ function getStaticPrerenderPaths(routes) {
3473
3473
  }
3474
3474
  }
3475
3475
  recurse(routes);
3476
- return {
3477
- paths: paths.map((p) => p.replace(/\/\/+/g, "/").replace(/(.+)\/$/, "$1")),
3478
- paramRoutes
3479
- };
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"));
3480
3486
  }
3481
3487
  async function prerenderData(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
3482
3488
  let normalizedPath = `${reactRouterConfig.basename}${prerenderPath === "/" ? "/_root.data" : `${prerenderPath.replace(/\/$/, "")}.data`}`.replace(/\/\/+/g, "/");
3483
3489
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
3484
3490
  let response = await handler(request);
3485
3491
  let data = await response.text();
3486
- if (response.status !== 200) {
3487
- throw new Error(
3488
- `Prerender: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${path6}\` path.
3489
- ${normalizedPath}`
3490
- );
3491
- }
3492
+ validatePrerenderedResponse(response, data, "Prerender", normalizedPath);
3492
3493
  let outdir = path6.relative(process.cwd(), clientBuildDirectory);
3493
3494
  let outfile = path6.join(outdir, ...normalizedPath.split("/"));
3494
3495
  await fse.ensureDir(path6.dirname(outfile));
3495
3496
  await fse.outputFile(outfile, data);
3496
- viteConfig.logger.info(
3497
- `Prerender Data: ${prerenderPath} -> ${import_picocolors3.default.bold(outfile)}`
3498
- );
3497
+ viteConfig.logger.info(`Prerender: Generated ${import_picocolors3.default.bold(outfile)}`);
3499
3498
  return data;
3500
3499
  }
3501
3500
  async function prerenderRoute(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
@@ -3506,65 +3505,42 @@ async function prerenderRoute(handler, prerenderPath, clientBuildDirectory, reac
3506
3505
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
3507
3506
  let response = await handler(request);
3508
3507
  let html = await response.text();
3509
- if (response.status !== 200) {
3510
- throw new Error(
3511
- `Prerender: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${normalizedPath}\` path.
3512
- ${html}`
3513
- );
3508
+ validatePrerenderedResponse(response, html, "Prerender", normalizedPath);
3509
+ if (!reactRouterConfig.ssr) {
3510
+ validatePrerenderedHtml(html, "Prerender");
3514
3511
  }
3515
3512
  let outdir = path6.relative(process.cwd(), clientBuildDirectory);
3516
3513
  let outfile = path6.join(outdir, ...normalizedPath.split("/"), "index.html");
3517
3514
  await fse.ensureDir(path6.dirname(outfile));
3518
3515
  await fse.outputFile(outfile, html);
3519
- viteConfig.logger.info(
3520
- `Prerender: ${prerenderPath} -> ${import_picocolors3.default.bold(outfile)}`
3521
- );
3516
+ viteConfig.logger.info(`Prerender: Generated ${import_picocolors3.default.bold(outfile)}`);
3522
3517
  }
3523
3518
  async function prerenderResourceRoute(handler, prerenderPath, clientBuildDirectory, reactRouterConfig, viteConfig, requestInit) {
3524
3519
  let normalizedPath = `${reactRouterConfig.basename}${prerenderPath}/`.replace(/\/\/+/g, "/").replace(/\/$/g, "");
3525
3520
  let request = new Request(`http://localhost${normalizedPath}`, requestInit);
3526
3521
  let response = await handler(request);
3527
3522
  let text = await response.text();
3528
- if (response.status !== 200) {
3529
- throw new Error(
3530
- `Prerender: Received a ${response.status} status code from \`entry.server.tsx\` while prerendering the \`${normalizedPath}\` path.
3531
- ${text}`
3532
- );
3533
- }
3523
+ validatePrerenderedResponse(response, text, "Prerender", normalizedPath);
3534
3524
  let outdir = path6.relative(process.cwd(), clientBuildDirectory);
3535
3525
  let outfile = path6.join(outdir, ...normalizedPath.split("/"));
3536
3526
  await fse.ensureDir(path6.dirname(outfile));
3537
3527
  await fse.outputFile(outfile, text);
3538
- viteConfig.logger.info(
3539
- `Prerender: ${prerenderPath} -> ${import_picocolors3.default.bold(outfile)}`
3540
- );
3528
+ viteConfig.logger.info(`Prerender: Generated ${import_picocolors3.default.bold(outfile)}`);
3541
3529
  }
3542
- async function getPrerenderPaths(prerender, ssr, routes) {
3543
- let prerenderPaths = [];
3544
- if (prerender != null && prerender !== false) {
3545
- let prerenderRoutes = createPrerenderRoutes(routes);
3546
- if (prerender === true) {
3547
- let { paths, paramRoutes } = getStaticPrerenderPaths(prerenderRoutes);
3548
- if (!ssr && paramRoutes.length > 0) {
3549
- console.warn(
3550
- import_picocolors3.default.yellow(
3551
- [
3552
- "\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:",
3553
- ...paramRoutes.map((p) => " - " + p)
3554
- ].join("\n")
3555
- )
3556
- );
3557
- }
3558
- prerenderPaths = paths;
3559
- } else if (typeof prerender === "function") {
3560
- prerenderPaths = await prerender({
3561
- getStaticPaths: () => getStaticPrerenderPaths(prerenderRoutes).paths
3562
- });
3563
- } else {
3564
- prerenderPaths = prerender || ["/"];
3565
- }
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
+ );
3536
+ }
3537
+ }
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
+ );
3566
3543
  }
3567
- return prerenderPaths;
3568
3544
  }
3569
3545
  function groupRoutesByParentId2(manifest) {
3570
3546
  let routes = {};
@@ -3582,16 +3558,19 @@ function groupRoutesByParentId2(manifest) {
3582
3558
  function createPrerenderRoutes(manifest, parentId = "", routesByParentId = groupRoutesByParentId2(manifest)) {
3583
3559
  return (routesByParentId[parentId] || []).map((route) => {
3584
3560
  let commonRoute = {
3561
+ // Always include root due to default boundaries
3562
+ hasErrorBoundary: route.id === "root" || route.module.ErrorBoundary != null,
3585
3563
  id: route.id,
3586
- path: route.path
3564
+ path: route.path,
3565
+ loader: route.module.loader ? () => null : void 0,
3566
+ action: void 0,
3567
+ handle: route.module.handle
3587
3568
  };
3588
- if (route.index) {
3589
- return {
3590
- index: true,
3591
- ...commonRoute
3592
- };
3593
- }
3594
- return {
3569
+ return route.index ? {
3570
+ index: true,
3571
+ ...commonRoute
3572
+ } : {
3573
+ caseSensitive: route.caseSensitive,
3595
3574
  children: createPrerenderRoutes(manifest, route.id, routesByParentId),
3596
3575
  ...commonRoute
3597
3576
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-router/dev",
3
- "version": "0.0.0-experimental-b9cf540fe",
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-b9cf540fe"
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-b9cf540fe",
121
- "@react-router/serve": "0.0.0-experimental-b9cf540fe"
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-b9cf540fe",
128
- "react-router": "^0.0.0-experimental-b9cf540fe"
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": {