@react-router/dev 7.9.3-pre.0 → 7.9.4-pre.0

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,13 +1,123 @@
1
1
  # `@react-router/dev`
2
2
 
3
- ## 7.9.3-pre.0
3
+ ## 7.9.4-pre.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Update `valibot` dependency to `^1.1.0` ([#14379](https://github.com/remix-run/react-router/pull/14379))
8
+ - New (unstable) `useRoute` hook for accessing data from specific routes ([#14407](https://github.com/remix-run/react-router/pull/14407))
9
+
10
+ For example, let's say you have an `admin` route somewhere in your app and you want any child routes of `admin` to all have access to the `loaderData` and `actionData` from `admin.`
11
+
12
+ ```tsx
13
+ // app/routes/admin.tsx
14
+ import { Outlet } from "react-router";
15
+
16
+ export const loader = () => ({ message: "Hello, loader!" });
17
+
18
+ export const action = () => ({ count: 1 });
19
+
20
+ export default function Component() {
21
+ return (
22
+ <div>
23
+ {/* ... */}
24
+ <Outlet />
25
+ {/* ... */}
26
+ </div>
27
+ );
28
+ }
29
+ ```
30
+
31
+ You might even want to create a reusable widget that all of the routes nested under `admin` could use:
32
+
33
+ ```tsx
34
+ import { unstable_useRoute as useRoute } from "react-router";
35
+
36
+ export function AdminWidget() {
37
+ // How to get `message` and `count` from `admin` route?
38
+ }
39
+ ```
40
+
41
+ In framework mode, `useRoute` knows all your app's routes and gives you TS errors when invalid route IDs are passed in:
42
+
43
+ ```tsx
44
+ export function AdminWidget() {
45
+ const admin = useRoute("routes/dmin");
46
+ // ^^^^^^^^^^^
47
+ }
48
+ ```
49
+
50
+ `useRoute` returns `undefined` if the route is not part of the current page:
51
+
52
+ ```tsx
53
+ export function AdminWidget() {
54
+ const admin = useRoute("routes/admin");
55
+ if (!admin) {
56
+ throw new Error(`AdminWidget used outside of "routes/admin"`);
57
+ }
58
+ }
59
+ ```
60
+
61
+ Note: the `root` route is the exception since it is guaranteed to be part of the current page.
62
+ As a result, `useRoute` never returns `undefined` for `root`.
63
+
64
+ `loaderData` and `actionData` are marked as optional since they could be accessed before the `action` is triggered or after the `loader` threw an error:
65
+
66
+ ```tsx
67
+ export function AdminWidget() {
68
+ const admin = useRoute("routes/admin");
69
+ if (!admin) {
70
+ throw new Error(`AdminWidget used outside of "routes/admin"`);
71
+ }
72
+ const { loaderData, actionData } = admin;
73
+ console.log(loaderData);
74
+ // ^? { message: string } | undefined
75
+ console.log(actionData);
76
+ // ^? { count: number } | undefined
77
+ }
78
+ ```
79
+
80
+ If instead of a specific route, you wanted access to the _current_ route's `loaderData` and `actionData`, you can call `useRoute` without arguments:
81
+
82
+ ```tsx
83
+ export function AdminWidget() {
84
+ const currentRoute = useRoute();
85
+ currentRoute.loaderData;
86
+ currentRoute.actionData;
87
+ }
88
+ ```
89
+
90
+ This usage is equivalent to calling `useLoaderData` and `useActionData`, but consolidates all route data access into one hook: `useRoute`.
91
+
92
+ Note: when calling `useRoute()` (without a route ID), TS has no way to know which route is the current route.
93
+ As a result, `loaderData` and `actionData` are typed as `unknown`.
94
+ If you want more type-safety, you can either narrow the type yourself with something like `zod` or you can refactor your app to pass down typed props to your `AdminWidget`:
95
+
96
+ ```tsx
97
+ export function AdminWidget({
98
+ message,
99
+ count,
100
+ }: {
101
+ message: string;
102
+ count: number;
103
+ }) {
104
+ /* ... */
105
+ }
106
+ ```
107
+
108
+ - Updated dependencies:
109
+ - `react-router@7.9.4-pre.0`
110
+ - `@react-router/node@7.9.4-pre.0`
111
+ - `@react-router/serve@7.9.4-pre.0`
112
+
113
+ ## 7.9.3
4
114
 
5
115
  ### Patch Changes
6
116
 
7
117
  - Updated dependencies:
8
- - `react-router@7.9.3-pre.0`
9
- - `@react-router/node@7.9.3-pre.0`
10
- - `@react-router/serve@7.9.3-pre.0`
118
+ - `react-router@7.9.3`
119
+ - `@react-router/node@7.9.3`
120
+ - `@react-router/serve@7.9.3`
11
121
 
12
122
  ## 7.9.2
13
123
 
package/dist/cli/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @react-router/dev v7.9.3-pre.0
3
+ * @react-router/dev v7.9.4-pre.0
4
4
  *
5
5
  * Copyright (c) Remix Software Inc.
6
6
  *
@@ -980,9 +980,10 @@ function generateRoutes(ctx) {
980
980
  interface Register {
981
981
  pages: Pages
982
982
  routeFiles: RouteFiles
983
+ routeModules: RouteModules
983
984
  }
984
985
  }
985
- ` + "\n\n" + generate(pagesType(allPages)).code + "\n\n" + generate(routeFilesType({ fileToRoutes, routeToPages })).code
986
+ ` + "\n\n" + generate(pagesType(allPages)).code + "\n\n" + generate(routeFilesType({ fileToRoutes, routeToPages })).code + "\n\n" + generate(routeModulesType(ctx)).code
986
987
  };
987
988
  const allAnnotations = Array.from(fileToRoutes.entries()).filter(([file]) => isInAppDirectory(ctx, file)).map(
988
989
  ([file, routeIds]) => getRouteAnnotations({ ctx, file, routeIds, lineages })
@@ -1051,6 +1052,28 @@ function routeFilesType({
1051
1052
  )
1052
1053
  );
1053
1054
  }
1055
+ function routeModulesType(ctx) {
1056
+ return t2.tsTypeAliasDeclaration(
1057
+ t2.identifier("RouteModules"),
1058
+ null,
1059
+ t2.tsTypeLiteral(
1060
+ Object.values(ctx.config.routes).map(
1061
+ (route) => t2.tsPropertySignature(
1062
+ t2.stringLiteral(route.id),
1063
+ t2.tsTypeAnnotation(
1064
+ t2.tsTypeQuery(
1065
+ t2.tsImportType(
1066
+ t2.stringLiteral(
1067
+ `./${Path3.relative(ctx.rootDirectory, ctx.config.appDirectory)}/${route.file}`
1068
+ )
1069
+ )
1070
+ )
1071
+ )
1072
+ )
1073
+ )
1074
+ )
1075
+ );
1076
+ }
1054
1077
  function isInAppDirectory(ctx, routeFile) {
1055
1078
  const path9 = Path3.resolve(ctx.config.appDirectory, routeFile);
1056
1079
  return path9.startsWith(ctx.config.appDirectory);
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v7.9.3-pre.0
2
+ * @react-router/dev v7.9.4-pre.0
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 v7.9.3-pre.0
2
+ * @react-router/dev v7.9.4-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v7.9.3-pre.0
2
+ * @react-router/dev v7.9.4-pre.0
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 v7.9.3-pre.0
2
+ * @react-router/dev v7.9.4-pre.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -968,9 +968,10 @@ function generateRoutes(ctx) {
968
968
  interface Register {
969
969
  pages: Pages
970
970
  routeFiles: RouteFiles
971
+ routeModules: RouteModules
971
972
  }
972
973
  }
973
- ` + "\n\n" + generate(pagesType(allPages)).code + "\n\n" + generate(routeFilesType({ fileToRoutes, routeToPages })).code
974
+ ` + "\n\n" + generate(pagesType(allPages)).code + "\n\n" + generate(routeFilesType({ fileToRoutes, routeToPages })).code + "\n\n" + generate(routeModulesType(ctx)).code
974
975
  };
975
976
  const allAnnotations = Array.from(fileToRoutes.entries()).filter(([file]) => isInAppDirectory(ctx, file)).map(
976
977
  ([file, routeIds]) => getRouteAnnotations({ ctx, file, routeIds, lineages })
@@ -1039,6 +1040,28 @@ function routeFilesType({
1039
1040
  )
1040
1041
  );
1041
1042
  }
1043
+ function routeModulesType(ctx) {
1044
+ return t2.tsTypeAliasDeclaration(
1045
+ t2.identifier("RouteModules"),
1046
+ null,
1047
+ t2.tsTypeLiteral(
1048
+ Object.values(ctx.config.routes).map(
1049
+ (route) => t2.tsPropertySignature(
1050
+ t2.stringLiteral(route.id),
1051
+ t2.tsTypeAnnotation(
1052
+ t2.tsTypeQuery(
1053
+ t2.tsImportType(
1054
+ t2.stringLiteral(
1055
+ `./${Path3.relative(ctx.rootDirectory, ctx.config.appDirectory)}/${route.file}`
1056
+ )
1057
+ )
1058
+ )
1059
+ )
1060
+ )
1061
+ )
1062
+ )
1063
+ );
1064
+ }
1042
1065
  function isInAppDirectory(ctx, routeFile) {
1043
1066
  const path9 = Path3.resolve(ctx.config.appDirectory, routeFile);
1044
1067
  return path9.startsWith(ctx.config.appDirectory);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-router/dev",
3
- "version": "7.9.3-pre.0",
3
+ "version": "7.9.4-pre.0",
4
4
  "description": "Dev tools and CLI for React Router",
5
5
  "homepage": "https://reactrouter.com",
6
6
  "bugs": {
@@ -84,9 +84,9 @@
84
84
  "react-refresh": "^0.14.0",
85
85
  "semver": "^7.3.7",
86
86
  "tinyglobby": "^0.2.14",
87
- "valibot": "^0.41.0",
87
+ "valibot": "^1.1.0",
88
88
  "vite-node": "^3.2.2",
89
- "@react-router/node": "7.9.3-pre.0"
89
+ "@react-router/node": "7.9.4-pre.0"
90
90
  },
91
91
  "devDependencies": {
92
92
  "@types/babel__core": "^7.20.5",
@@ -109,16 +109,16 @@
109
109
  "vite": "^6.1.0",
110
110
  "wireit": "0.14.9",
111
111
  "wrangler": "^4.23.0",
112
- "@react-router/serve": "7.9.3-pre.0",
113
- "react-router": "^7.9.3-pre.0"
112
+ "@react-router/serve": "7.9.4-pre.0",
113
+ "react-router": "^7.9.4-pre.0"
114
114
  },
115
115
  "peerDependencies": {
116
116
  "@vitejs/plugin-rsc": "*",
117
117
  "typescript": "^5.1.0",
118
118
  "vite": "^5.1.0 || ^6.0.0 || ^7.0.0",
119
119
  "wrangler": "^3.28.2 || ^4.0.0",
120
- "@react-router/serve": "^7.9.3-pre.0",
121
- "react-router": "^7.9.3-pre.0"
120
+ "@react-router/serve": "^7.9.4-pre.0",
121
+ "react-router": "^7.9.4-pre.0"
122
122
  },
123
123
  "peerDependenciesMeta": {
124
124
  "@vitejs/plugin-rsc": {