@react-router/dev 7.6.1 → 7.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,67 @@
1
1
  # `@react-router/dev`
2
2
 
3
+ ## 7.6.2
4
+
5
+ ### Patch Changes
6
+
7
+ - Avoid additional `with-props` chunk in Framework Mode by moving route module component prop logic from the Vite plugin to `react-router` ([#13650](https://github.com/remix-run/react-router/pull/13650))
8
+
9
+ - When `future.unstable_viteEnvironmentApi` is enabled and an absolute Vite `base` has been configured, ensure critical CSS is handled correctly during development ([#13598](https://github.com/remix-run/react-router/pull/13598))
10
+
11
+ - Update `vite-node` ([#13673](https://github.com/remix-run/react-router/pull/13673))
12
+
13
+ - Fix typegen for non-{.js,.jsx,.ts,.tsx} routes like .mdx ([#12453](https://github.com/remix-run/react-router/pull/12453))
14
+
15
+ - Fix href types for optional dynamic params ([#13725](https://github.com/remix-run/react-router/pull/13725))
16
+
17
+ 7.6.1 introduced fixes for `href` when using optional static segments,
18
+ but those fixes caused regressions with how optional dynamic params worked in 7.6.0:
19
+
20
+ ```ts
21
+ // 7.6.0
22
+ href("/users/:id?"); // ✅
23
+ href("/users/:id?", { id: 1 }); // ✅
24
+
25
+ // 7.6.1
26
+ href("/users/:id?"); // ❌
27
+ href("/users/:id?", { id: 1 }); // ❌
28
+ ```
29
+
30
+ Now, optional static segments are expanded into different paths for `href`, but optional dynamic params are not.
31
+ This way `href` can unambiguously refer to an exact URL path, all while keeping the number of path options to a minimum.
32
+
33
+ ```ts
34
+ // 7.6.2
35
+
36
+ // path: /users/:id?/edit?
37
+ href("
38
+ // ^ suggestions when cursor is here:
39
+ //
40
+ // /users/:id?
41
+ // /users/:id?/edit
42
+ ```
43
+
44
+ Additionally, you can pass `params` from component props without needing to narrow them manually:
45
+
46
+ ```ts
47
+ declare const params: { id?: number };
48
+
49
+ // 7.6.0
50
+ href("/users/:id?", params);
51
+
52
+ // 7.6.1
53
+ href("/users/:id?", params); // ❌
54
+ "id" in params ? href("/users/:id", params) : href("/users"); // works... but is annoying
55
+
56
+ // 7.6.2
57
+ href("/users/:id?", params); // restores behavior of 7.6.0
58
+ ```
59
+
60
+ - Updated dependencies:
61
+ - `react-router@7.6.2`
62
+ - `@react-router/node@7.6.2`
63
+ - `@react-router/serve@7.6.2`
64
+
3
65
  ## 7.6.1
4
66
 
5
67
  ### Patch Changes
package/dist/cli/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
- * @react-router/dev v7.6.1
3
+ * @react-router/dev v7.6.2
4
4
  *
5
5
  * Copyright (c) Remix Software Inc.
6
6
  *
@@ -928,7 +928,7 @@ function generateRoutes(ctx) {
928
928
  lineages.set(route.id, lineage2);
929
929
  const fullpath2 = fullpath(lineage2);
930
930
  if (!fullpath2) continue;
931
- const pages = explodeOptionalSegments(fullpath2);
931
+ const pages = expand(fullpath2);
932
932
  pages.forEach((page) => allPages.add(page));
933
933
  lineage2.forEach(({ id }) => {
934
934
  let routePages = routeToPages.get(id);
@@ -1136,9 +1136,13 @@ function getRouteAnnotations({
1136
1136
  }
1137
1137
  function relativeImportSource(from, to) {
1138
1138
  let path8 = Path3.relative(Path3.dirname(from), to);
1139
+ let extension = Path3.extname(path8);
1139
1140
  path8 = Path3.join(Path3.dirname(path8), Pathe.filename(path8));
1140
1141
  if (!path8.startsWith("../")) path8 = "./" + path8;
1141
- return path8 + ".js";
1142
+ if (!extension || /\.(js|ts)x?$/.test(extension)) {
1143
+ extension = ".js";
1144
+ }
1145
+ return path8 + extension;
1142
1146
  }
1143
1147
  function rootDirsPath(ctx, typesPath) {
1144
1148
  const rel = Path3.relative(typesDirectory(ctx), typesPath);
@@ -1157,28 +1161,27 @@ function paramsType(path8) {
1157
1161
  })
1158
1162
  );
1159
1163
  }
1160
- function explodeOptionalSegments(path8) {
1161
- let segments = path8.split("/");
1162
- if (segments.length === 0) return [];
1163
- let [first, ...rest] = segments;
1164
- let isOptional = first.endsWith("?");
1165
- let required = first.replace(/\?$/, "");
1166
- if (rest.length === 0) {
1167
- return isOptional ? [required, ""] : [required];
1168
- }
1169
- let restExploded = explodeOptionalSegments(rest.join("/"));
1170
- let result = [];
1171
- result.push(
1172
- ...restExploded.map(
1173
- (subpath) => subpath === "" ? required : [required, subpath].join("/")
1174
- )
1175
- );
1176
- if (isOptional) {
1177
- result.push(...restExploded);
1178
- }
1179
- return result.map(
1180
- (exploded) => path8.startsWith("/") && exploded === "" ? "/" : exploded
1181
- );
1164
+ function expand(fullpath2) {
1165
+ function recurse(segments2, index) {
1166
+ if (index === segments2.length) return [""];
1167
+ const segment = segments2[index];
1168
+ const isOptional = segment.endsWith("?");
1169
+ const isDynamic = segment.startsWith(":");
1170
+ const required = segment.replace(/\?$/, "");
1171
+ const keep = !isOptional || isDynamic;
1172
+ const kept = isDynamic ? segment : required;
1173
+ const withoutSegment = recurse(segments2, index + 1);
1174
+ const withSegment = withoutSegment.map((rest) => [kept, rest].join("/"));
1175
+ if (keep) return withSegment;
1176
+ return [...withoutSegment, ...withSegment];
1177
+ }
1178
+ const segments = fullpath2.split("/");
1179
+ const expanded = /* @__PURE__ */ new Set();
1180
+ for (let result of recurse(segments, 0)) {
1181
+ if (result !== "/") result = result.replace(/\/$/, "");
1182
+ expanded.add(result);
1183
+ }
1184
+ return expanded;
1182
1185
  }
1183
1186
  var import_dedent, Path3, Pathe, t2;
1184
1187
  var init_generate = __esm({
@@ -1392,14 +1395,10 @@ var init_route_chunks = __esm({
1392
1395
  });
1393
1396
 
1394
1397
  // vite/with-props.ts
1395
- var import_dedent2, vmod;
1396
1398
  var init_with_props = __esm({
1397
1399
  "vite/with-props.ts"() {
1398
1400
  "use strict";
1399
- import_dedent2 = __toESM(require("dedent"));
1400
1401
  init_babel();
1401
- init_virtual_module();
1402
- vmod = create("with-props");
1403
1402
  }
1404
1403
  });
1405
1404
 
@@ -1802,7 +1801,7 @@ async function viteAppBuild(root, {
1802
1801
  },
1803
1802
  configResolved(config) {
1804
1803
  let hasReactRouterPlugin = config.plugins.find(
1805
- (plugin2) => plugin2.name === "react-router"
1804
+ (plugin) => plugin.name === "react-router"
1806
1805
  );
1807
1806
  if (!hasReactRouterPlugin) {
1808
1807
  throw new Error(
@@ -1941,7 +1940,7 @@ async function dev(root, {
1941
1940
  clearScreen,
1942
1941
  logLevel
1943
1942
  });
1944
- if (!server.config.plugins.find((plugin2) => plugin2.name === "react-router")) {
1943
+ if (!server.config.plugins.find((plugin) => plugin.name === "react-router")) {
1945
1944
  console.error(
1946
1945
  import_picocolors6.default.red("React Router Vite plugin not found in Vite config")
1947
1946
  );
package/dist/config.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v7.6.1
2
+ * @react-router/dev v7.6.2
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.6.1
2
+ * @react-router/dev v7.6.2
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @react-router/dev v7.6.1
2
+ * @react-router/dev v7.6.2
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.6.1
2
+ * @react-router/dev v7.6.2
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -915,7 +915,7 @@ function generateRoutes(ctx) {
915
915
  lineages.set(route.id, lineage2);
916
916
  const fullpath2 = fullpath(lineage2);
917
917
  if (!fullpath2) continue;
918
- const pages = explodeOptionalSegments(fullpath2);
918
+ const pages = expand(fullpath2);
919
919
  pages.forEach((page) => allPages.add(page));
920
920
  lineage2.forEach(({ id }) => {
921
921
  let routePages = routeToPages.get(id);
@@ -1123,9 +1123,13 @@ function getRouteAnnotations({
1123
1123
  }
1124
1124
  function relativeImportSource(from, to) {
1125
1125
  let path6 = Path3.relative(Path3.dirname(from), to);
1126
+ let extension = Path3.extname(path6);
1126
1127
  path6 = Path3.join(Path3.dirname(path6), Pathe.filename(path6));
1127
1128
  if (!path6.startsWith("../")) path6 = "./" + path6;
1128
- return path6 + ".js";
1129
+ if (!extension || /\.(js|ts)x?$/.test(extension)) {
1130
+ extension = ".js";
1131
+ }
1132
+ return path6 + extension;
1129
1133
  }
1130
1134
  function rootDirsPath(ctx, typesPath) {
1131
1135
  const rel = Path3.relative(typesDirectory(ctx), typesPath);
@@ -1144,28 +1148,27 @@ function paramsType(path6) {
1144
1148
  })
1145
1149
  );
1146
1150
  }
1147
- function explodeOptionalSegments(path6) {
1148
- let segments = path6.split("/");
1149
- if (segments.length === 0) return [];
1150
- let [first, ...rest] = segments;
1151
- let isOptional = first.endsWith("?");
1152
- let required = first.replace(/\?$/, "");
1153
- if (rest.length === 0) {
1154
- return isOptional ? [required, ""] : [required];
1151
+ function expand(fullpath2) {
1152
+ function recurse(segments2, index) {
1153
+ if (index === segments2.length) return [""];
1154
+ const segment = segments2[index];
1155
+ const isOptional = segment.endsWith("?");
1156
+ const isDynamic = segment.startsWith(":");
1157
+ const required = segment.replace(/\?$/, "");
1158
+ const keep = !isOptional || isDynamic;
1159
+ const kept = isDynamic ? segment : required;
1160
+ const withoutSegment = recurse(segments2, index + 1);
1161
+ const withSegment = withoutSegment.map((rest) => [kept, rest].join("/"));
1162
+ if (keep) return withSegment;
1163
+ return [...withoutSegment, ...withSegment];
1155
1164
  }
1156
- let restExploded = explodeOptionalSegments(rest.join("/"));
1157
- let result = [];
1158
- result.push(
1159
- ...restExploded.map(
1160
- (subpath) => subpath === "" ? required : [required, subpath].join("/")
1161
- )
1162
- );
1163
- if (isOptional) {
1164
- result.push(...restExploded);
1165
+ const segments = fullpath2.split("/");
1166
+ const expanded = /* @__PURE__ */ new Set();
1167
+ for (let result of recurse(segments, 0)) {
1168
+ if (result !== "/") result = result.replace(/\/$/, "");
1169
+ expanded.add(result);
1165
1170
  }
1166
- return result.map(
1167
- (exploded) => path6.startsWith("/") && exploded === "" ? "/" : exploded
1168
- );
1171
+ return expanded;
1169
1172
  }
1170
1173
 
1171
1174
  // typegen/index.ts
@@ -2207,59 +2210,11 @@ function getRouteChunkNameFromModuleId(id) {
2207
2210
  }
2208
2211
 
2209
2212
  // vite/with-props.ts
2210
- var import_dedent2 = __toESM(require("dedent"));
2211
- var vmod = create("with-props");
2212
- var NAMED_COMPONENT_EXPORTS = ["HydrateFallback", "ErrorBoundary"];
2213
- var plugin = {
2214
- name: "react-router-with-props",
2215
- enforce: "pre",
2216
- resolveId(id) {
2217
- if (id === vmod.id) return vmod.resolvedId;
2218
- },
2219
- async load(id) {
2220
- if (id !== vmod.resolvedId) return;
2221
- return import_dedent2.default`
2222
- import { createElement as h } from "react";
2223
- import { useActionData, useLoaderData, useMatches, useParams, useRouteError } from "react-router";
2224
-
2225
- export function withComponentProps(Component) {
2226
- return function Wrapped() {
2227
- const props = {
2228
- params: useParams(),
2229
- loaderData: useLoaderData(),
2230
- actionData: useActionData(),
2231
- matches: useMatches(),
2232
- };
2233
- return h(Component, props);
2234
- };
2235
- }
2236
-
2237
- export function withHydrateFallbackProps(HydrateFallback) {
2238
- return function Wrapped() {
2239
- const props = {
2240
- params: useParams(),
2241
- loaderData: useLoaderData(),
2242
- actionData: useActionData(),
2243
- };
2244
- return h(HydrateFallback, props);
2245
- };
2246
- }
2247
-
2248
- export function withErrorBoundaryProps(ErrorBoundary) {
2249
- return function Wrapped() {
2250
- const props = {
2251
- params: useParams(),
2252
- loaderData: useLoaderData(),
2253
- actionData: useActionData(),
2254
- error: useRouteError(),
2255
- };
2256
- return h(ErrorBoundary, props);
2257
- };
2258
- }
2259
- `;
2260
- }
2261
- };
2262
- var transform = (ast) => {
2213
+ var namedComponentExports = ["HydrateFallback", "ErrorBoundary"];
2214
+ function isNamedComponentExport(name) {
2215
+ return namedComponentExports.includes(name);
2216
+ }
2217
+ var decorateComponentExportsWithProps = (ast) => {
2263
2218
  const hocs = [];
2264
2219
  function getHocUid(path6, hocName) {
2265
2220
  const uid = path6.scope.generateUidIdentifier(hocName);
@@ -2272,7 +2227,7 @@ var transform = (ast) => {
2272
2227
  const declaration = path6.get("declaration");
2273
2228
  const expr = declaration.isExpression() ? declaration.node : declaration.isFunctionDeclaration() ? toFunctionExpression(declaration.node) : void 0;
2274
2229
  if (expr) {
2275
- const uid = getHocUid(path6, "withComponentProps");
2230
+ const uid = getHocUid(path6, "UNSAFE_withComponentProps");
2276
2231
  declaration.replaceWith(t.callExpression(uid, [expr]));
2277
2232
  }
2278
2233
  return;
@@ -2287,8 +2242,8 @@ var transform = (ast) => {
2287
2242
  if (!expr) return;
2288
2243
  if (!id.isIdentifier()) return;
2289
2244
  const { name } = id.node;
2290
- if (!NAMED_COMPONENT_EXPORTS.includes(name)) return;
2291
- const uid = getHocUid(path6, `with${name}Props`);
2245
+ if (!isNamedComponentExport(name)) return;
2246
+ const uid = getHocUid(path6, `UNSAFE_with${name}Props`);
2292
2247
  init.replaceWith(t.callExpression(uid, [expr]));
2293
2248
  });
2294
2249
  return;
@@ -2297,8 +2252,8 @@ var transform = (ast) => {
2297
2252
  const { id } = decl.node;
2298
2253
  if (!id) return;
2299
2254
  const { name } = id;
2300
- if (!NAMED_COMPONENT_EXPORTS.includes(name)) return;
2301
- const uid = getHocUid(path6, `with${name}Props`);
2255
+ if (!isNamedComponentExport(name)) return;
2256
+ const uid = getHocUid(path6, `UNSAFE_with${name}Props`);
2302
2257
  decl.replaceWith(
2303
2258
  t.variableDeclaration("const", [
2304
2259
  t.variableDeclarator(
@@ -2317,7 +2272,7 @@ var transform = (ast) => {
2317
2272
  hocs.map(
2318
2273
  ([name, identifier]) => t.importSpecifier(identifier, t.identifier(name))
2319
2274
  ),
2320
- t.stringLiteral(vmod.id)
2275
+ t.stringLiteral("react-router")
2321
2276
  )
2322
2277
  );
2323
2278
  }
@@ -2414,8 +2369,8 @@ var virtual = {
2414
2369
  browserManifest: create("browser-manifest")
2415
2370
  };
2416
2371
  var invalidateVirtualModules = (viteDevServer) => {
2417
- Object.values(virtual).forEach((vmod2) => {
2418
- let mod = viteDevServer.moduleGraph.getModuleById(vmod2.resolvedId);
2372
+ Object.values(virtual).forEach((vmod) => {
2373
+ let mod = viteDevServer.moduleGraph.getModuleById(vmod.resolvedId);
2419
2374
  if (mod) {
2420
2375
  viteDevServer.moduleGraph.invalidateModule(mod);
2421
2376
  }
@@ -2649,7 +2604,7 @@ var reactRouterVitePlugin = () => {
2649
2604
  };
2650
2605
  let pluginIndex = (pluginName) => {
2651
2606
  invariant(viteConfig);
2652
- return viteConfig.plugins.findIndex((plugin2) => plugin2.name === pluginName);
2607
+ return viteConfig.plugins.findIndex((plugin) => plugin.name === pluginName);
2653
2608
  };
2654
2609
  let getServerEntry = async ({ routeIds }) => {
2655
2610
  invariant(viteConfig, "viteconfig required to generate the server entry");
@@ -3235,9 +3190,9 @@ var reactRouterVitePlugin = () => {
3235
3190
  envFile: false,
3236
3191
  plugins: [
3237
3192
  childCompilerPlugins.filter(
3238
- (plugin2) => typeof plugin2 === "object" && plugin2 !== null && "name" in plugin2 && plugin2.name !== "react-router" && plugin2.name !== "react-router:route-exports" && plugin2.name !== "react-router:hmr-updates"
3239
- ).map((plugin2) => ({
3240
- ...plugin2,
3193
+ (plugin) => typeof plugin === "object" && plugin !== null && "name" in plugin && plugin.name !== "react-router" && plugin.name !== "react-router:route-exports" && plugin.name !== "react-router:hmr-updates"
3194
+ ).map((plugin) => ({
3195
+ ...plugin,
3241
3196
  configureServer: void 0,
3242
3197
  configurePreviewServer: void 0
3243
3198
  }))
@@ -3319,7 +3274,7 @@ var reactRouterVitePlugin = () => {
3319
3274
  if (ctx.reactRouterConfig.future.unstable_viteEnvironmentApi) {
3320
3275
  viteDevServer.middlewares.use(async (req, res, next) => {
3321
3276
  let [reqPathname, reqSearch] = (req.url ?? "").split("?");
3322
- if (reqPathname === `${ctx.publicPath}@react-router/critical.css`) {
3277
+ if (reqPathname.endsWith("/@react-router/critical.css")) {
3323
3278
  let pathname = new URLSearchParams(reqSearch).get("pathname");
3324
3279
  if (!pathname) {
3325
3280
  return next("No pathname provided");
@@ -3627,8 +3582,8 @@ var reactRouterVitePlugin = () => {
3627
3582
  name: "react-router:virtual-modules",
3628
3583
  enforce: "pre",
3629
3584
  resolveId(id) {
3630
- const vmod2 = Object.values(virtual).find((vmod3) => vmod3.id === id);
3631
- if (vmod2) return vmod2.resolvedId;
3585
+ const vmod = Object.values(virtual).find((vmod2) => vmod2.id === id);
3586
+ if (vmod) return vmod.resolvedId;
3632
3587
  },
3633
3588
  async load(id) {
3634
3589
  switch (id) {
@@ -3739,7 +3694,6 @@ var reactRouterVitePlugin = () => {
3739
3694
  }
3740
3695
  }
3741
3696
  },
3742
- plugin,
3743
3697
  {
3744
3698
  name: "react-router:route-exports",
3745
3699
  async transform(code, id, options) {
@@ -3776,7 +3730,7 @@ var reactRouterVitePlugin = () => {
3776
3730
  if (!options?.ssr) {
3777
3731
  removeExports(ast, SERVER_ONLY_ROUTE_EXPORTS);
3778
3732
  }
3779
- transform(ast);
3733
+ decorateComponentExportsWithProps(ast);
3780
3734
  return generate(ast, {
3781
3735
  sourceMaps: true,
3782
3736
  filename: id,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@react-router/dev",
3
- "version": "7.6.1",
3
+ "version": "7.6.2",
4
4
  "description": "Dev tools and CLI for React Router",
5
5
  "homepage": "https://reactrouter.com",
6
6
  "bugs": {
@@ -85,8 +85,8 @@
85
85
  "semver": "^7.3.7",
86
86
  "set-cookie-parser": "^2.6.0",
87
87
  "valibot": "^0.41.0",
88
- "vite-node": "3.0.0-beta.2",
89
- "@react-router/node": "7.6.1"
88
+ "vite-node": "^3.1.4",
89
+ "@react-router/node": "7.6.2"
90
90
  },
91
91
  "devDependencies": {
92
92
  "@types/babel__core": "^7.20.5",
@@ -110,15 +110,15 @@
110
110
  "vite": "^6.1.0",
111
111
  "wireit": "0.14.9",
112
112
  "wrangler": "^4.2.0",
113
- "@react-router/serve": "7.6.1",
114
- "react-router": "^7.6.1"
113
+ "@react-router/serve": "7.6.2",
114
+ "react-router": "^7.6.2"
115
115
  },
116
116
  "peerDependencies": {
117
117
  "typescript": "^5.1.0",
118
118
  "vite": "^5.1.0 || ^6.0.0",
119
119
  "wrangler": "^3.28.2 || ^4.0.0",
120
- "@react-router/serve": "^7.6.1",
121
- "react-router": "^7.6.1"
120
+ "@react-router/serve": "^7.6.2",
121
+ "react-router": "^7.6.2"
122
122
  },
123
123
  "peerDependenciesMeta": {
124
124
  "@react-router/serve": {