react-router 8.2.0 → 8.3.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.
Files changed (169) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/development/dom-export.js +1 -1
  3. package/dist/development/index-react-server-client.js +1 -1
  4. package/dist/development/index-react-server.d.ts +106 -17
  5. package/dist/development/index-react-server.js +168 -42
  6. package/dist/development/index.js +1 -1
  7. package/dist/development/lib/actions.js +1 -1
  8. package/dist/development/lib/components.js +1 -1
  9. package/dist/development/lib/context.js +1 -1
  10. package/dist/development/lib/dom/dom.d.ts +24 -24
  11. package/dist/development/lib/dom/dom.js +25 -25
  12. package/dist/development/lib/dom/lib.d.ts +2 -1
  13. package/dist/development/lib/dom/lib.js +5 -4
  14. package/dist/development/lib/dom/server.js +1 -1
  15. package/dist/development/lib/dom/ssr/components.js +1 -1
  16. package/dist/development/lib/dom/ssr/data.js +1 -1
  17. package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
  18. package/dist/development/lib/dom/ssr/fallback.js +1 -1
  19. package/dist/development/lib/dom/ssr/fog-of-war.js +26 -21
  20. package/dist/development/lib/dom/ssr/hydration.js +1 -1
  21. package/dist/development/lib/dom/ssr/invariant.js +1 -1
  22. package/dist/development/lib/dom/ssr/links.js +1 -1
  23. package/dist/development/lib/dom/ssr/markup.js +1 -1
  24. package/dist/development/lib/dom/ssr/routeModules.js +1 -1
  25. package/dist/development/lib/dom/ssr/routes-test-stub.d.ts +13 -0
  26. package/dist/development/lib/dom/ssr/routes-test-stub.js +14 -1
  27. package/dist/development/lib/dom/ssr/routes.js +1 -1
  28. package/dist/development/lib/dom/ssr/server.js +1 -1
  29. package/dist/development/lib/dom/ssr/single-fetch.js +1 -1
  30. package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
  31. package/dist/development/lib/dom-export/hydrated-router.js +1 -1
  32. package/dist/development/lib/errors.js +1 -1
  33. package/dist/development/lib/hooks.js +1 -1
  34. package/dist/development/lib/href.d.ts +24 -8
  35. package/dist/development/lib/href.js +28 -11
  36. package/dist/development/lib/router/history.d.ts +6 -0
  37. package/dist/development/lib/router/history.js +7 -1
  38. package/dist/development/lib/router/instrumentation.js +1 -1
  39. package/dist/development/lib/router/router.js +1 -1
  40. package/dist/development/lib/router/url.js +1 -1
  41. package/dist/development/lib/router/utils.d.ts +12 -0
  42. package/dist/development/lib/router/utils.js +57 -3
  43. package/dist/development/lib/rsc/browser.js +34 -24
  44. package/dist/development/lib/rsc/errorBoundaries.js +1 -1
  45. package/dist/development/lib/rsc/html-stream/browser.js +1 -1
  46. package/dist/development/lib/rsc/html-stream/server.js +29 -10
  47. package/dist/development/lib/rsc/route-modules.js +1 -1
  48. package/dist/development/lib/rsc/server.rsc.d.ts +5 -1
  49. package/dist/development/lib/rsc/server.ssr.d.ts +29 -6
  50. package/dist/development/lib/rsc/server.ssr.js +27 -12
  51. package/dist/development/lib/server-runtime/cookies.d.ts +28 -2
  52. package/dist/development/lib/server-runtime/cookies.js +17 -3
  53. package/dist/development/lib/server-runtime/crypto.js +1 -1
  54. package/dist/development/lib/server-runtime/data.js +1 -1
  55. package/dist/development/lib/server-runtime/dev.js +1 -1
  56. package/dist/development/lib/server-runtime/entry.js +1 -1
  57. package/dist/development/lib/server-runtime/errors.js +1 -1
  58. package/dist/development/lib/server-runtime/headers.js +1 -1
  59. package/dist/development/lib/server-runtime/invariant.js +1 -1
  60. package/dist/development/lib/server-runtime/mode.js +1 -1
  61. package/dist/development/lib/server-runtime/routeMatching.js +1 -1
  62. package/dist/development/lib/server-runtime/routes.js +1 -1
  63. package/dist/development/lib/server-runtime/server.d.ts +12 -0
  64. package/dist/development/lib/server-runtime/server.js +13 -1
  65. package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
  66. package/dist/development/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
  67. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +9 -1
  68. package/dist/development/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
  69. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +12 -6
  70. package/dist/development/lib/server-runtime/sessions.d.ts +31 -2
  71. package/dist/development/lib/server-runtime/sessions.js +20 -3
  72. package/dist/development/lib/server-runtime/single-fetch.js +1 -1
  73. package/dist/development/lib/server-runtime/urls.js +1 -1
  74. package/dist/development/lib/server-runtime/warnings.js +1 -1
  75. package/dist/development/lib/types/internal.js +1 -1
  76. package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
  77. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  78. package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
  79. package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
  80. package/dist/production/dom-export.js +1 -1
  81. package/dist/production/index-react-server-client.js +1 -1
  82. package/dist/production/index-react-server.d.ts +106 -17
  83. package/dist/production/index-react-server.js +168 -42
  84. package/dist/production/index.js +1 -1
  85. package/dist/production/lib/actions.js +1 -1
  86. package/dist/production/lib/components.js +1 -1
  87. package/dist/production/lib/context.js +1 -1
  88. package/dist/production/lib/dom/dom.d.ts +24 -24
  89. package/dist/production/lib/dom/dom.js +25 -25
  90. package/dist/production/lib/dom/lib.d.ts +2 -1
  91. package/dist/production/lib/dom/lib.js +5 -4
  92. package/dist/production/lib/dom/server.js +1 -1
  93. package/dist/production/lib/dom/ssr/components.js +1 -1
  94. package/dist/production/lib/dom/ssr/data.js +1 -1
  95. package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
  96. package/dist/production/lib/dom/ssr/fallback.js +1 -1
  97. package/dist/production/lib/dom/ssr/fog-of-war.js +26 -21
  98. package/dist/production/lib/dom/ssr/hydration.js +1 -1
  99. package/dist/production/lib/dom/ssr/invariant.js +1 -1
  100. package/dist/production/lib/dom/ssr/links.js +1 -1
  101. package/dist/production/lib/dom/ssr/markup.js +1 -1
  102. package/dist/production/lib/dom/ssr/routeModules.js +1 -1
  103. package/dist/production/lib/dom/ssr/routes-test-stub.d.ts +13 -0
  104. package/dist/production/lib/dom/ssr/routes-test-stub.js +14 -1
  105. package/dist/production/lib/dom/ssr/routes.js +1 -1
  106. package/dist/production/lib/dom/ssr/server.js +1 -1
  107. package/dist/production/lib/dom/ssr/single-fetch.js +1 -1
  108. package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
  109. package/dist/production/lib/dom-export/hydrated-router.js +1 -1
  110. package/dist/production/lib/errors.js +1 -1
  111. package/dist/production/lib/hooks.js +1 -1
  112. package/dist/production/lib/href.d.ts +24 -8
  113. package/dist/production/lib/href.js +28 -11
  114. package/dist/production/lib/router/history.d.ts +6 -0
  115. package/dist/production/lib/router/history.js +7 -1
  116. package/dist/production/lib/router/instrumentation.js +1 -1
  117. package/dist/production/lib/router/router.js +1 -1
  118. package/dist/production/lib/router/url.js +1 -1
  119. package/dist/production/lib/router/utils.d.ts +12 -0
  120. package/dist/production/lib/router/utils.js +57 -3
  121. package/dist/production/lib/rsc/browser.js +34 -24
  122. package/dist/production/lib/rsc/errorBoundaries.js +1 -1
  123. package/dist/production/lib/rsc/html-stream/browser.js +1 -1
  124. package/dist/production/lib/rsc/html-stream/server.js +29 -10
  125. package/dist/production/lib/rsc/route-modules.js +1 -1
  126. package/dist/production/lib/rsc/server.rsc.d.ts +5 -1
  127. package/dist/production/lib/rsc/server.ssr.d.ts +29 -6
  128. package/dist/production/lib/rsc/server.ssr.js +27 -12
  129. package/dist/production/lib/server-runtime/cookies.d.ts +28 -2
  130. package/dist/production/lib/server-runtime/cookies.js +17 -3
  131. package/dist/production/lib/server-runtime/crypto.js +1 -1
  132. package/dist/production/lib/server-runtime/data.js +1 -1
  133. package/dist/production/lib/server-runtime/dev.js +1 -1
  134. package/dist/production/lib/server-runtime/entry.js +1 -1
  135. package/dist/production/lib/server-runtime/errors.js +1 -1
  136. package/dist/production/lib/server-runtime/headers.js +1 -1
  137. package/dist/production/lib/server-runtime/invariant.js +1 -1
  138. package/dist/production/lib/server-runtime/mode.js +1 -1
  139. package/dist/production/lib/server-runtime/routeMatching.js +1 -1
  140. package/dist/production/lib/server-runtime/routes.js +1 -1
  141. package/dist/production/lib/server-runtime/server.d.ts +12 -0
  142. package/dist/production/lib/server-runtime/server.js +13 -1
  143. package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
  144. package/dist/production/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
  145. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +9 -1
  146. package/dist/production/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
  147. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +12 -6
  148. package/dist/production/lib/server-runtime/sessions.d.ts +31 -2
  149. package/dist/production/lib/server-runtime/sessions.js +20 -3
  150. package/dist/production/lib/server-runtime/single-fetch.js +1 -1
  151. package/dist/production/lib/server-runtime/urls.js +1 -1
  152. package/dist/production/lib/server-runtime/warnings.js +1 -1
  153. package/dist/production/lib/types/internal.js +1 -1
  154. package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
  155. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  156. package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
  157. package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
  158. package/docs/explanation/sessions-and-cookies.md +13 -13
  159. package/docs/explanation/state-management.md +2 -2
  160. package/docs/how-to/presets.md +2 -2
  161. package/docs/how-to/react-server-components.md +53 -1
  162. package/docs/how-to/security.md +8 -1
  163. package/docs/how-to/server-bundles.md +2 -2
  164. package/docs/start/data/route-object.md +2 -2
  165. package/docs/start/data/routing.md +1 -1
  166. package/docs/start/framework/pending-ui.md +1 -1
  167. package/docs/start/framework/route-module.md +8 -8
  168. package/docs/start/framework/routing.md +1 -1
  169. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -110,6 +110,29 @@ function getManifestPath(_manifestPath, basename) {
110
110
  return basename == null ? manifestPath : joinPaths([basename, manifestPath]);
111
111
  }
112
112
  const MANIFEST_VERSION_STORAGE_KEY = "react-router-manifest-version";
113
+ async function handleClientVersionMismatch(needsReload, version, errorReloadPath) {
114
+ if (!needsReload) {
115
+ try {
116
+ sessionStorage.removeItem(MANIFEST_VERSION_STORAGE_KEY);
117
+ } catch {}
118
+ return false;
119
+ }
120
+ if (!errorReloadPath) {
121
+ console.warn("Detected a manifest version mismatch during eager route discovery. The next navigation/fetch to an undiscovered route will result in a new document navigation to sync up with the latest manifest.");
122
+ return true;
123
+ }
124
+ try {
125
+ if (sessionStorage.getItem(MANIFEST_VERSION_STORAGE_KEY) === version) {
126
+ console.error("Unable to discover routes due to manifest version mismatch.");
127
+ return true;
128
+ }
129
+ sessionStorage.setItem(MANIFEST_VERSION_STORAGE_KEY, version);
130
+ } catch {}
131
+ window.location.href = errorReloadPath;
132
+ console.warn("Detected manifest version mismatch, reloading...");
133
+ await new Promise(() => {});
134
+ return true;
135
+ }
113
136
  async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, routeModules, ssr, isSpaMode, basename, manifestPath, patchRoutes, signal) {
114
137
  paths = getPathsWithAncestors(paths);
115
138
  const searchParams = new URLSearchParams();
@@ -125,25 +148,7 @@ async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, ro
125
148
  try {
126
149
  let res = await fetch(url, { signal });
127
150
  if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
128
- else if (res.status === 204 && res.headers.has("X-Remix-Reload-Document")) {
129
- if (!errorReloadPath) {
130
- console.warn("Detected a manifest version mismatch during eager route discovery. The next navigation/fetch to an undiscovered route will result in a new document navigation to sync up with the latest manifest.");
131
- return;
132
- }
133
- try {
134
- if (sessionStorage.getItem(MANIFEST_VERSION_STORAGE_KEY) === manifest.version) {
135
- console.error("Unable to discover routes due to manifest version mismatch.");
136
- return;
137
- }
138
- sessionStorage.setItem(MANIFEST_VERSION_STORAGE_KEY, manifest.version);
139
- } catch {}
140
- window.location.href = errorReloadPath;
141
- console.warn("Detected manifest version mismatch, reloading...");
142
- await new Promise(() => {});
143
- } else if (res.status >= 400) throw new Error(await res.text());
144
- try {
145
- sessionStorage.removeItem(MANIFEST_VERSION_STORAGE_KEY);
146
- } catch {}
151
+ if (await handleClientVersionMismatch(res.status === 204 && res.headers.has("X-Remix-Reload-Document"), manifest.version, errorReloadPath)) return;
147
152
  serverPatches = await res.json();
148
153
  } catch (e) {
149
154
  if (signal?.aborted) return;
@@ -177,4 +182,4 @@ function debounce(callback, wait) {
177
182
  };
178
183
  }
179
184
  //#endregion
180
- export { URL_LIMIT, getManifestPath, getPartialManifest, getPatchRoutesOnNavigationFunction, getPathsWithAncestors, isFogOfWarEnabled, useFogOFWarDiscovery };
185
+ export { URL_LIMIT, getManifestPath, getPartialManifest, getPatchRoutesOnNavigationFunction, getPathsWithAncestors, handleClientVersionMismatch, isFogOfWarEnabled, useFogOFWarDiscovery };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -50,7 +50,20 @@ interface RoutesTestStubProps {
50
50
  future?: Partial<FutureConfig>;
51
51
  }
52
52
  /**
53
+ * Creates a React component that renders the provided routes in a test-friendly
54
+ * React Router context.
55
+ *
56
+ * Use this to unit test components that rely on router context, such as
57
+ * `loaderData`, `actionData`, and route matches.
58
+ *
59
+ * @public
53
60
  * @category Utils
61
+ * @mode framework
62
+ * @mode data
63
+ * @param routes The route objects to render in the test router.
64
+ * @param _context An optional {@link RouterContextProvider} for supplying
65
+ * application context values to route middleware, loaders, and actions.
66
+ * @returns A React component that renders the test router.
54
67
  */
55
68
  declare function createRoutesStub(routes: StubRouteObject[], _context?: RouterContextProvider): ({
56
69
  initialEntries,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -14,7 +14,20 @@ import { FrameworkContext } from "./components.js";
14
14
  import * as React$1 from "react";
15
15
  //#region lib/dom/ssr/routes-test-stub.tsx
16
16
  /**
17
+ * Creates a React component that renders the provided routes in a test-friendly
18
+ * React Router context.
19
+ *
20
+ * Use this to unit test components that rely on router context, such as
21
+ * `loaderData`, `actionData`, and route matches.
22
+ *
23
+ * @public
17
24
  * @category Utils
25
+ * @mode framework
26
+ * @mode data
27
+ * @param routes The route objects to render in the test router.
28
+ * @param _context An optional {@link RouterContextProvider} for supplying
29
+ * application context values to route middleware, loaders, and actions.
30
+ * @returns A React component that renders the test router.
18
31
  */
19
32
  function createRoutesStub(routes, _context) {
20
33
  return function RoutesTestStub({ initialEntries, initialIndex, hydrationData, future }) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -6,14 +6,30 @@ import { Pages } from "./types/register.js";
6
6
  type Args = { [K in keyof Pages]: ToArgs<Pages[K]["params"]> };
7
7
  type ToArgs<Params extends Record<string, string | undefined>> = Equal<Params, {}> extends true ? [] : Partial<Params> extends Params ? [Params] | [] : [Params];
8
8
  /**
9
- Returns a resolved URL path for the specified route.
10
-
11
- ```tsx
12
- const h = href("/:lang?/about", { lang: "en" })
13
- // -> `/en/about`
14
-
15
- <Link to={href("/products/:id", { id: "abc123" })} />
16
- ```
9
+ * Returns a resolved URL path for the specified route.
10
+ *
11
+ * Param values are percent-encoded for use in a path segment: characters that
12
+ * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
13
+ * are escaped, while characters that RFC 3986 allows literally in a path
14
+ * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
15
+ * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
16
+ * delimiters and must be escaped. Splat (`*`) values are encoded per segment,
17
+ * preserving `/` separators.
18
+ *
19
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
20
+ *
21
+ * @example
22
+ * const h = href("/:lang?/about", { lang: "en" })
23
+ * // -> `/en/about`
24
+ *
25
+ * <Link to={href("/products/:id", { id: "abc123" })} />
26
+ *
27
+ * @public
28
+ * @category Utils
29
+ * @mode framework
30
+ * @param path The route path to resolve
31
+ * @param args The route params to use when resolving the path
32
+ * @returns The resolved URL path
17
33
  */
18
34
  declare function href<Path extends keyof Args>(path: Path, ...args: Args[Path]): string;
19
35
  //#endregion
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -8,19 +8,36 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
+ import { encodePathParam } from "./router/utils.js";
11
12
  //#region lib/href.ts
12
13
  function stringify(p) {
13
14
  return p == null ? "" : typeof p === "string" ? p : String(p);
14
15
  }
15
16
  /**
16
- Returns a resolved URL path for the specified route.
17
-
18
- ```tsx
19
- const h = href("/:lang?/about", { lang: "en" })
20
- // -> `/en/about`
21
-
22
- <Link to={href("/products/:id", { id: "abc123" })} />
23
- ```
17
+ * Returns a resolved URL path for the specified route.
18
+ *
19
+ * Param values are percent-encoded for use in a path segment: characters that
20
+ * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
21
+ * are escaped, while characters that RFC 3986 allows literally in a path
22
+ * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
23
+ * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
24
+ * delimiters and must be escaped. Splat (`*`) values are encoded per segment,
25
+ * preserving `/` separators.
26
+ *
27
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
28
+ *
29
+ * @example
30
+ * const h = href("/:lang?/about", { lang: "en" })
31
+ * // -> `/en/about`
32
+ *
33
+ * <Link to={href("/products/:id", { id: "abc123" })} />
34
+ *
35
+ * @public
36
+ * @category Utils
37
+ * @mode framework
38
+ * @param path The route path to resolve
39
+ * @param args The route params to use when resolving the path
40
+ * @returns The resolved URL path
24
41
  */
25
42
  function href(path, ...args) {
26
43
  let params = args[0];
@@ -28,11 +45,11 @@ function href(path, ...args) {
28
45
  const isRequired = questionMark === void 0;
29
46
  const value = params?.[param];
30
47
  if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`);
31
- return value == null ? "" : "/" + encodeURIComponent(stringify(value));
48
+ return value == null ? "" : "/" + encodePathParam(stringify(value));
32
49
  });
33
50
  if (path.endsWith("*")) {
34
51
  const value = params?.["*"];
35
- if (value !== void 0) result += "/" + stringify(value).split("/").map(encodeURIComponent).join("/");
52
+ if (value !== void 0) result += "/" + stringify(value).split("/").map(encodePathParam).join("/");
36
53
  }
37
54
  return result || "/";
38
55
  }
@@ -236,7 +236,10 @@ declare function invariant<T>(value: T | null | undefined, message?: string): as
236
236
  /**
237
237
  * Creates a string URL path from the given pathname, search, and hash components.
238
238
  *
239
+ * @public
239
240
  * @category Utils
241
+ * @param path The pathname, search, and hash components to combine.
242
+ * @returns The combined URL path.
240
243
  */
241
244
  declare function createPath({
242
245
  pathname,
@@ -246,7 +249,10 @@ declare function createPath({
246
249
  /**
247
250
  * Parses a string URL path into its separate pathname, search, and hash components.
248
251
  *
252
+ * @public
249
253
  * @category Utils
254
+ * @param path The URL path to parse.
255
+ * @returns The parsed pathname, search, and hash components.
250
256
  */
251
257
  declare function parsePath(path: string): Partial<Path>;
252
258
  interface UrlHistory extends History {}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -231,7 +231,10 @@ function createLocation(current, to, state = null, key, mask) {
231
231
  /**
232
232
  * Creates a string URL path from the given pathname, search, and hash components.
233
233
  *
234
+ * @public
234
235
  * @category Utils
236
+ * @param path The pathname, search, and hash components to combine.
237
+ * @returns The combined URL path.
235
238
  */
236
239
  function createPath({ pathname = "/", search = "", hash = "" }) {
237
240
  if (search && search !== "?") pathname += search.charAt(0) === "?" ? search : "?" + search;
@@ -241,7 +244,10 @@ function createPath({ pathname = "/", search = "", hash = "" }) {
241
244
  /**
242
245
  * Parses a string URL path into its separate pathname, search, and hash components.
243
246
  *
247
+ * @public
244
248
  * @category Utils
249
+ * @param path The URL path to parse.
250
+ * @returns The parsed pathname, search, and hash components.
245
251
  */
246
252
  function parsePath(path) {
247
253
  let parsedPath = {};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -686,10 +686,22 @@ interface RouteBranch<RouteObjectType extends RouteObject = RouteObject> {
686
686
  /**
687
687
  * Returns a path with params interpolated.
688
688
  *
689
+ * Param values are percent-encoded for use in a path segment: characters that
690
+ * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
691
+ * are escaped, while characters that RFC 3986 allows literally in a path
692
+ * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
693
+ * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
694
+ * delimiters and must be escaped. Splat (`*`) values are encoded per segment,
695
+ * preserving `/` separators.
696
+ *
697
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
698
+ *
689
699
  * @example
690
700
  * import { generatePath } from "react-router";
691
701
  *
692
702
  * generatePath("/users/:id", { id: "123" }); // "/users/123"
703
+ * generatePath("/files/:name", { name: "a b" }); // "/files/a%20b"
704
+ * generatePath("/releases/:v", { v: "1.0.0+1" }); // "/releases/1.0.0+1"
693
705
  *
694
706
  * @public
695
707
  * @category Utils
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -366,12 +366,66 @@ function matchRouteBranch(branch, pathname, allowPartial = false) {
366
366
  return matches;
367
367
  }
368
368
  /**
369
+ * Characters that `encodeURIComponent` escapes but that are valid literally in
370
+ * a URL path segment. Per RFC 3986 §3.3, a path segment is made of `pchar`:
371
+ *
372
+ * ```
373
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
374
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
375
+ * ```
376
+ *
377
+ * `encodeURIComponent` targets query-string values, where `$ & + , ; = : @`
378
+ * are delimiters and must be escaped — but in a path segment they carry no
379
+ * special meaning, and browsers keep them literal in `location.pathname`.
380
+ * (`! ' ( ) *` and the unreserved set are already left alone by
381
+ * `encodeURIComponent`, so they need no restoring.)
382
+ */
383
+ const PATH_PARAM_OVERESCAPED = {
384
+ "%24": "$",
385
+ "%26": "&",
386
+ "%2B": "+",
387
+ "%2C": ",",
388
+ "%3A": ":",
389
+ "%3B": ";",
390
+ "%3D": "=",
391
+ "%40": "@"
392
+ };
393
+ /**
394
+ * Encodes a param value for interpolation into a single URL path segment.
395
+ *
396
+ * Escapes characters that would break the path (`/ ? # %`, whitespace,
397
+ * non-ASCII, …) while leaving characters that RFC 3986 permits literally in a
398
+ * path segment untouched. Escaping those would needlessly rewrite URLs — e.g.
399
+ * a semver build param `1.0.0+1` would become `1.0.0%2B1` even though browsers
400
+ * display and match the `+` literally in `location.pathname`.
401
+ *
402
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3))
403
+ *
404
+ * @param value The param value to encode.
405
+ * @returns The encoded value, safe for use as a single path segment.
406
+ */
407
+ function encodePathParam(value) {
408
+ return encodeURIComponent(value).replace(/%(?:24|26|2B|2C|3A|3B|3D|40)/g, (match) => PATH_PARAM_OVERESCAPED[match]);
409
+ }
410
+ /**
369
411
  * Returns a path with params interpolated.
370
412
  *
413
+ * Param values are percent-encoded for use in a path segment: characters that
414
+ * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
415
+ * are escaped, while characters that RFC 3986 allows literally in a path
416
+ * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
417
+ * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
418
+ * delimiters and must be escaped. Splat (`*`) values are encoded per segment,
419
+ * preserving `/` separators.
420
+ *
421
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
422
+ *
371
423
  * @example
372
424
  * import { generatePath } from "react-router";
373
425
  *
374
426
  * generatePath("/users/:id", { id: "123" }); // "/users/123"
427
+ * generatePath("/files/:name", { name: "a b" }); // "/files/a%20b"
428
+ * generatePath("/releases/:v", { v: "1.0.0+1" }); // "/releases/1.0.0+1"
375
429
  *
376
430
  * @public
377
431
  * @category Utils
@@ -394,7 +448,7 @@ function generatePath(originalPath, params = {}) {
394
448
  const [, key, optional, suffix] = keyMatch;
395
449
  let param = params[key];
396
450
  invariant(optional === "?" || param != null, `Missing ":${key}" param`);
397
- return encodeURIComponent(stringify(param)) + suffix;
451
+ return encodePathParam(stringify(param)) + suffix;
398
452
  }
399
453
  return segment.replace(/\?$/g, "");
400
454
  }).filter((segment) => !!segment).join("/");
@@ -815,4 +869,4 @@ function parseToInfo(_to, basename) {
815
869
  };
816
870
  }
817
871
  //#endregion
818
- export { ErrorResponseImpl, RouterContextProvider, SUPPORTED_ERROR_TYPES, compilePath, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createContext, createDataFunctionUrl, data, decodePath, defaultMapRouteProperties, flattenAndRankRoutes, generatePath, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isBrowser, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, joinPaths, matchPath, matchRoutes, matchRoutesImpl, parseToInfo, prependBasename, redirect, redirectDocument, removeDoubleSlashes, removeTrailingSlash, replace, resolvePath, resolveTo, stripBasename };
872
+ export { ErrorResponseImpl, RouterContextProvider, SUPPORTED_ERROR_TYPES, compilePath, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createContext, createDataFunctionUrl, data, decodePath, defaultMapRouteProperties, encodePathParam, flattenAndRankRoutes, generatePath, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isBrowser, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, joinPaths, matchPath, matchRoutes, matchRoutesImpl, parseToInfo, prependBasename, redirect, redirectDocument, removeDoubleSlashes, removeTrailingSlash, replace, resolvePath, resolveTo, stripBasename };