Skip to content

Commit cf2dea7

Browse files
committed
Merge branch 'dev' into markdalgleish/rsc-handle-changed-routes
2 parents 9f7f706 + 35069d5 commit cf2dea7

File tree

11 files changed

+140
-76
lines changed

11 files changed

+140
-76
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { defineConfig } from "vite";
2+
import rsc from "@vitejs/plugin-rsc";
23
import { __INTERNAL_DO_NOT_USE_OR_YOU_WILL_GET_A_STRONGLY_WORDED_LETTER__ } from "@react-router/dev/internal";
34

45
const { unstable_reactRouterRSC: reactRouterRSC } =
56
__INTERNAL_DO_NOT_USE_OR_YOU_WILL_GET_A_STRONGLY_WORDED_LETTER__;
67

78
export default defineConfig({
8-
plugins: [reactRouterRSC()],
9+
plugins: [reactRouterRSC(), rsc()],
910
});

integration/helpers/vite.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,14 @@ export const viteConfig = {
132132
`;
133133
},
134134
basic: async (args: ViteConfigArgs) => {
135+
const isRsc = args.templateName?.includes("rsc");
135136
return dedent`
136137
${
137-
!args.templateName?.includes("rsc")
138+
!isRsc
138139
? "import { reactRouter } from '@react-router/dev/vite';"
139140
: [
140141
"import { __INTERNAL_DO_NOT_USE_OR_YOU_WILL_GET_A_STRONGLY_WORDED_LETTER__ } from '@react-router/dev/internal';",
142+
"import rsc from '@vitejs/plugin-rsc';",
141143
"const { unstable_reactRouterRSC: reactRouter } = __INTERNAL_DO_NOT_USE_OR_YOU_WILL_GET_A_STRONGLY_WORDED_LETTER__;",
142144
].join("\n")
143145
}
@@ -155,6 +157,7 @@ export const viteConfig = {
155157
${args.mdx ? "mdx()," : ""}
156158
${args.vanillaExtract ? "vanillaExtractPlugin({ emitCssInSsr: true })," : ""}
157159
reactRouter(),
160+
${isRsc ? "rsc()," : ""}
158161
envOnlyMacros(),
159162
tsconfigPaths()
160163
],

integration/vite-plugin-order-validation-test.ts

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,57 +4,89 @@ import dedent from "dedent";
44
import { createProject, build, reactRouterConfig } from "./helpers/vite.js";
55

66
test.describe("Vite plugin order validation", () => {
7-
test.describe("MDX", () => {
8-
test("Framework Mode", async () => {
9-
let cwd = await createProject({
10-
"vite.config.ts": dedent`
11-
import { reactRouter } from "@react-router/dev/vite";
7+
test("Framework Mode with MDX plugin after React Router plugin", async () => {
8+
let cwd = await createProject({
9+
"vite.config.ts": dedent`
10+
import { reactRouter } from "@react-router/dev/vite";
11+
import mdx from "@mdx-js/rollup";
12+
13+
export default {
14+
plugins: [
15+
reactRouter(),
16+
mdx(),
17+
],
18+
}
19+
`,
20+
});
21+
22+
let buildResult = build({ cwd });
23+
expect(buildResult.stderr.toString()).toContain(
24+
'Error: The "@mdx-js/rollup" plugin should be placed before the React Router plugin in your Vite config',
25+
);
26+
});
27+
28+
test("RSC Framework Mode with MDX plugin after React Router plugin", async () => {
29+
let cwd = await createProject(
30+
{
31+
"vite.config.js": dedent`
32+
import { defineConfig } from "vite";
33+
import { __INTERNAL_DO_NOT_USE_OR_YOU_WILL_GET_A_STRONGLY_WORDED_LETTER__ } from "@react-router/dev/internal";
34+
import rsc from "@vitejs/plugin-rsc";
1235
import mdx from "@mdx-js/rollup";
1336
14-
export default {
37+
const { unstable_reactRouterRSC: reactRouterRSC } =
38+
__INTERNAL_DO_NOT_USE_OR_YOU_WILL_GET_A_STRONGLY_WORDED_LETTER__;
39+
40+
export default defineConfig({
1541
plugins: [
16-
reactRouter(),
42+
reactRouterRSC(),
43+
rsc(),
1744
mdx(),
1845
],
19-
}
46+
});
2047
`,
21-
});
48+
"react-router.config.ts": reactRouterConfig({
49+
viteEnvironmentApi: true,
50+
}),
51+
},
52+
"rsc-vite-framework",
53+
);
2254

23-
let buildResult = build({ cwd });
24-
expect(buildResult.stderr.toString()).toContain(
25-
'Error: The "@mdx-js/rollup" plugin should be placed before the React Router plugin in your Vite config file',
26-
);
27-
});
55+
let buildResult = build({ cwd });
56+
expect(buildResult.stderr.toString()).toContain(
57+
'Error: The "@mdx-js/rollup" plugin should be placed before the React Router plugin in your Vite config',
58+
);
59+
});
2860

29-
test("RSC Framework Mode", async () => {
30-
let cwd = await createProject(
31-
{
32-
"vite.config.js": dedent`
61+
test("RSC Framework Mode with @vitejs/plugin-rsc before React Router plugin", async () => {
62+
let cwd = await createProject(
63+
{
64+
"vite.config.js": dedent`
3365
import { defineConfig } from "vite";
3466
import { __INTERNAL_DO_NOT_USE_OR_YOU_WILL_GET_A_STRONGLY_WORDED_LETTER__ } from "@react-router/dev/internal";
67+
import rsc from "@vitejs/plugin-rsc";
3568
import mdx from "@mdx-js/rollup";
3669
3770
const { unstable_reactRouterRSC: reactRouterRSC } =
3871
__INTERNAL_DO_NOT_USE_OR_YOU_WILL_GET_A_STRONGLY_WORDED_LETTER__;
3972
4073
export default defineConfig({
4174
plugins: [
75+
rsc(),
4276
reactRouterRSC(),
43-
mdx(),
4477
],
4578
});
4679
`,
47-
"react-router.config.ts": reactRouterConfig({
48-
viteEnvironmentApi: true,
49-
}),
50-
},
51-
"rsc-vite-framework",
52-
);
53-
54-
let buildResult = build({ cwd });
55-
expect(buildResult.stderr.toString()).toContain(
56-
'Error: The "@mdx-js/rollup" plugin should be placed before the React Router plugin in your Vite config file',
57-
);
58-
});
80+
"react-router.config.ts": reactRouterConfig({
81+
viteEnvironmentApi: true,
82+
}),
83+
},
84+
"rsc-vite-framework",
85+
);
86+
87+
let buildResult = build({ cwd });
88+
expect(buildResult.stderr.toString()).toContain(
89+
'Error: The "@vitejs/plugin-rsc" plugin should be placed after the React Router RSC plugin in your Vite config',
90+
);
5991
});
6092
});

packages/react-router-dev/package.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@
7878
"@babel/types": "^7.27.7",
7979
"@npmcli/package-json": "^4.0.1",
8080
"@react-router/node": "workspace:*",
81-
"@vitejs/plugin-rsc": "0.4.26",
8281
"arg": "^5.0.1",
8382
"babel-dead-code-elimination": "^1.0.6",
8483
"chokidar": "^4.0.0",
@@ -110,6 +109,7 @@
110109
"@types/npmcli__package-json": "^4.0.0",
111110
"@types/set-cookie-parser": "^2.4.1",
112111
"@types/semver": "^7.7.0",
112+
"@vitejs/plugin-rsc": "0.4.26",
113113
"esbuild-register": "^3.6.0",
114114
"execa": "5.1.1",
115115
"express": "^4.19.2",
@@ -123,12 +123,16 @@
123123
},
124124
"peerDependencies": {
125125
"@react-router/serve": "workspace:^",
126+
"@vitejs/plugin-rsc": "*",
126127
"react-router": "workspace:^",
127128
"typescript": "^5.1.0",
128129
"vite": "^5.1.0 || ^6.0.0 || ^7.0.0",
129130
"wrangler": "^3.28.2 || ^4.0.0"
130131
},
131132
"peerDependenciesMeta": {
133+
"@vitejs/plugin-rsc": {
134+
"optional": true
135+
},
132136
"@react-router/serve": {
133137
"optional": true
134138
},
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
export function hasDependency({
2+
name,
3+
rootDirectory,
4+
}: {
5+
name: string;
6+
rootDirectory: string;
7+
}) {
8+
try {
9+
return Boolean(require.resolve(name, { paths: [rootDirectory] }));
10+
} catch (err) {
11+
return false;
12+
}
13+
}

packages/react-router-dev/vite/plugin.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import { resolveFileUrl } from "./resolve-file-url";
5858
import { combineURLs } from "./combine-urls";
5959
import { removeExports } from "./remove-exports";
6060
import { ssrExternals } from "./ssr-externals";
61+
import { hasDependency } from "./has-dependency";
6162
import {
6263
type RouteChunkName,
6364
type RouteChunkExportName,
@@ -819,14 +820,6 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
819820
return JSON.parse(manifestContents) as Vite.Manifest;
820821
};
821822

822-
let hasDependency = (name: string) => {
823-
try {
824-
return Boolean(require.resolve(name, { paths: [ctx.rootDirectory] }));
825-
} catch (err) {
826-
return false;
827-
}
828-
};
829-
830823
let getViteManifestAssetPaths = (
831824
viteManifest: Vite.Manifest,
832825
): Set<string> => {
@@ -1293,7 +1286,10 @@ export const reactRouterVitePlugin: ReactRouterVitePlugin = () => {
12931286
"react-router",
12941287
"react-router/dom",
12951288
// Check to avoid "Failed to resolve dependency: react-router-dom, present in 'optimizeDeps.include'"
1296-
...(hasDependency("react-router-dom")
1289+
...(hasDependency({
1290+
name: "react-router-dom",
1291+
rootDirectory: ctx.rootDirectory,
1292+
})
12971293
? ["react-router-dom"]
12981294
: []),
12991295
],

packages/react-router-dev/vite/plugins/validate-plugin-order.ts

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,27 @@ export default function validatePluginOrder(): Vite.Plugin {
1111
);
1212
};
1313

14-
let rollupPrePlugins = [
15-
{ pluginName: "@mdx-js/rollup", displayName: "@mdx-js/rollup" },
16-
];
17-
for (let prePlugin of rollupPrePlugins) {
18-
let prePluginIndex = pluginIndex(prePlugin.pluginName);
19-
if (
20-
prePluginIndex >= 0 &&
21-
prePluginIndex > pluginIndex(["react-router", "react-router/rsc"])
22-
) {
23-
throw new Error(
24-
`The "${prePlugin.displayName}" plugin should be placed before the React Router plugin in your Vite config file`,
25-
);
26-
}
14+
let reactRouterRscPluginIndex = pluginIndex("react-router/rsc");
15+
let viteRscPluginIndex = pluginIndex("rsc");
16+
if (
17+
reactRouterRscPluginIndex >= 0 &&
18+
viteRscPluginIndex >= 0 &&
19+
reactRouterRscPluginIndex > viteRscPluginIndex
20+
) {
21+
throw new Error(
22+
`The "@vitejs/plugin-rsc" plugin should be placed after the React Router RSC plugin in your Vite config`,
23+
);
24+
}
25+
26+
let reactRouterPluginIndex = pluginIndex([
27+
"react-router",
28+
"react-router/rsc",
29+
]);
30+
let mdxPluginIndex = pluginIndex("@mdx-js/rollup");
31+
if (mdxPluginIndex >= 0 && mdxPluginIndex > reactRouterPluginIndex) {
32+
throw new Error(
33+
`The "@mdx-js/rollup" plugin should be placed before the React Router plugin in your Vite config`,
34+
);
2735
}
2836
},
2937
};

packages/react-router-dev/vite/rsc/plugin.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import type * as Vite from "vite";
2-
import rsc, { type RscPluginOptions } from "@vitejs/plugin-rsc";
32
import { init as initEsModuleLexer } from "es-module-lexer";
43
import * as babel from "@babel/core";
54
import colors from "picocolors";
@@ -14,6 +13,7 @@ import {
1413
type ResolvedReactRouterConfig,
1514
createConfigLoader,
1615
} from "../../config/config";
16+
import { hasDependency } from "../has-dependency";
1717
import { createVirtualRouteConfig } from "./virtual-route-config";
1818
import {
1919
transformVirtualRouteModules,
@@ -66,6 +66,8 @@ export function reactRouterRSCVitePlugin(): Vite.PluginOption[] {
6666
prefix: "[react-router]",
6767
});
6868

69+
const rscEntries = getRscEntries();
70+
6971
return {
7072
resolve: {
7173
dedupe: [
@@ -76,7 +78,9 @@ export function reactRouterRSCVitePlugin(): Vite.PluginOption[] {
7678
// You must render this element inside a <Remix> element`.
7779
"react-router",
7880
"react-router/dom",
79-
"react-router-dom",
81+
...(hasDependency({ name: "react-router-dom", rootDirectory })
82+
? ["react-router-dom"]
83+
: []),
8084
],
8185
},
8286
optimizeDeps: {
@@ -92,6 +96,7 @@ export function reactRouterRSCVitePlugin(): Vite.PluginOption[] {
9296
"react/jsx-dev-runtime",
9397
"react-dom",
9498
"react-dom/client",
99+
"react-router/internal/react-server-client",
95100
],
96101
},
97102
esbuild: {
@@ -101,16 +106,19 @@ export function reactRouterRSCVitePlugin(): Vite.PluginOption[] {
101106
environments: {
102107
client: {
103108
build: {
109+
rollupOptions: { input: { index: rscEntries.client } },
104110
outDir: join(config.buildDirectory, "client"),
105111
},
106112
},
107113
rsc: {
108114
build: {
115+
rollupOptions: { input: { index: rscEntries.rsc } },
109116
outDir: join(config.buildDirectory, "server"),
110117
},
111118
},
112119
ssr: {
113120
build: {
121+
rollupOptions: { input: { index: rscEntries.ssr } },
114122
outDir: join(config.buildDirectory, "server/__ssr_build"),
115123
},
116124
},
@@ -407,7 +415,6 @@ export function reactRouterRSCVitePlugin(): Vite.PluginOption[] {
407415
},
408416
},
409417
validatePluginOrder(),
410-
rsc({ entries: getRscEntries() }),
411418
];
412419
}
413420

@@ -433,7 +440,11 @@ function getRootDirectory(viteUserConfig: Vite.UserConfig) {
433440
return viteUserConfig.root ?? process.env.REACT_ROUTER_ROOT ?? process.cwd();
434441
}
435442

436-
function getRscEntries(): NonNullable<RscPluginOptions["entries"]> {
443+
function getRscEntries(): {
444+
client: string;
445+
rsc: string;
446+
ssr: string;
447+
} {
437448
const entriesDir = join(
438449
getDevPackageRoot(),
439450
"dist",

playground/rsc-vite-framework/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
"@types/node": "^22.13.1",
1818
"@types/react": "^19.1.8",
1919
"@types/react-dom": "^19.1.6",
20-
"@vitejs/plugin-react": "^4.5.2",
2120
"@vitejs/plugin-rsc": "0.4.26",
2221
"cross-env": "^7.0.3",
2322
"remark-frontmatter": "^5.0.0",
Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11
import { defineConfig } from "vite";
22
import { __INTERNAL_DO_NOT_USE_OR_YOU_WILL_GET_A_STRONGLY_WORDED_LETTER__ } from "@react-router/dev/internal";
3+
import rsc from "@vitejs/plugin-rsc";
34
import mdx from "@mdx-js/rollup";
45
import remarkFrontmatter from "remark-frontmatter";
56
import remarkMdxFrontmatter from "remark-mdx-frontmatter";
67

7-
const { unstable_reactRouterRSC: reactRouterRSC } =
8+
const { unstable_reactRouterRSC: reactRouterRsc } =
89
__INTERNAL_DO_NOT_USE_OR_YOU_WILL_GET_A_STRONGLY_WORDED_LETTER__;
910

1011
export default defineConfig({
1112
plugins: [
12-
mdx({
13-
remarkPlugins: [remarkFrontmatter, remarkMdxFrontmatter],
14-
}),
15-
reactRouterRSC(),
13+
mdx({ remarkPlugins: [remarkFrontmatter, remarkMdxFrontmatter] }),
14+
reactRouterRsc(),
15+
rsc(),
1616
],
1717
});

0 commit comments

Comments
 (0)